document-adapter 0.7.1__tar.gz → 0.7.2__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.
Files changed (25) hide show
  1. {document_adapter-0.7.1/document_adapter.egg-info → document_adapter-0.7.2}/PKG-INFO +65 -20
  2. {document_adapter-0.7.1 → document_adapter-0.7.2}/README.md +64 -19
  3. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/tools.py +19 -5
  4. {document_adapter-0.7.1 → document_adapter-0.7.2/document_adapter.egg-info}/PKG-INFO +65 -20
  5. {document_adapter-0.7.1 → document_adapter-0.7.2}/pyproject.toml +1 -1
  6. {document_adapter-0.7.1 → document_adapter-0.7.2}/LICENSE +0 -0
  7. {document_adapter-0.7.1 → document_adapter-0.7.2}/NOTICE +0 -0
  8. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/__init__.py +0 -0
  9. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/base.py +0 -0
  10. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/docx_adapter.py +0 -0
  11. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_adapter.py +0 -0
  12. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_core/__init__.py +0 -0
  13. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_core/constants.py +0 -0
  14. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_core/grid.py +0 -0
  15. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_core/package.py +0 -0
  16. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/hwpx_core/paragraph.py +0 -0
  17. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/mcp_server.py +0 -0
  18. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter/pptx_adapter.py +0 -0
  19. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter.egg-info/SOURCES.txt +0 -0
  20. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter.egg-info/dependency_links.txt +0 -0
  21. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter.egg-info/entry_points.txt +0 -0
  22. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter.egg-info/requires.txt +0 -0
  23. {document_adapter-0.7.1 → document_adapter-0.7.2}/document_adapter.egg-info/top_level.txt +0 -0
  24. {document_adapter-0.7.1 → document_adapter-0.7.2}/setup.cfg +0 -0
  25. {document_adapter-0.7.1 → document_adapter-0.7.2}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: document-adapter
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: LLM-friendly document template editing (DOCX/PPTX/HWPX) with MCP server and Claude API tool-use support
5
5
  Author-email: Son Seongjun <sonsj97@plateer.com>
6
6
  License: MIT
@@ -127,13 +127,46 @@ doc.append_to_cell(table_index=2, row=0, col=0, value="홍길동")
127
127
  cell = doc.get_cell(table_index=1, row=3, col=2)
128
128
  print(cell.text, cell.is_anchor, cell.span, cell.nested_table_indices)
129
129
 
130
- # DOCX/HWPX는 행 추가 지원
130
+ # DOCX/PPTX/HWPX 전부 행 추가 지원 (v0.5+)
131
131
  doc.append_row(1, ["새 항목", "값"])
132
132
 
133
133
  doc.save("checklist_filled.docx")
134
134
  doc.close()
135
135
  ```
136
136
 
137
+ ### 라벨 기반 일괄 채우기 (v0.7+)
138
+
139
+ LLM 이 좌표 `(table_index, row, col)` 를 직접 계산하지 않고 "접수번호", "성명" 같은 사람이 읽는 라벨 key-value 로 양식을 채울 수 있습니다.
140
+
141
+ ```python
142
+ doc = load("form.hwpx")
143
+ result = doc.fill_form({
144
+ "접수번호": "2026-0001",
145
+ "성 명": "홍길동",
146
+ "주 소": "서울시 강남구",
147
+ "금융회사": "국민은행",
148
+ })
149
+ # → {"filled": [...], "not_found": [...], "ambiguous": [...]}
150
+ doc.save()
151
+ doc.close()
152
+ ```
153
+
154
+ - `auto` (기본): 라벨 셀 오른쪽 → 아래 → 같은 셀 순으로 값 셀 탐색. 보수적이라 기존 값 있는 셀은 다른 라벨로 간주하고 skip.
155
+ - `direction="right"` 명시: 라벨 오른쪽 셀을 **덮어쓰기** (예시값 있는 PPTX 템플릿 등).
156
+ - **Dot-path 섹션 지정**: 동일 라벨이 여러 섹션에 있으면 `"피해자.금액"`, `"지급정지요청계좌.금액"` 처럼 섹션 힌트 부여. `ambiguous` 반환 시 `hint` 필드에 예시 제공.
157
+ - **팁**: 한 양식의 관련 라벨을 한 번에 dict 로 넘기면 라벨끼리 서로 보호되어 오염을 방지합니다.
158
+
159
+ ### 셀 크기 메타 (v0.6+)
160
+
161
+ `get_tables()`가 `column_widths_cm` / `row_heights_cm` 를, `get_cell()`이 `width_cm` / `height_cm` / `char_count` 를 반환해 LLM이 **좁은 셀에 긴 텍스트를 넣어 오버플로 되는 것을 사전에 판단**할 수 있습니다.
162
+
163
+ ```python
164
+ cell = doc.get_cell(table_index=0, row=0, col=0)
165
+ print(cell.width_cm, cell.char_count) # 1.7cm, 4자 — 작은 배지
166
+ ```
167
+
168
+ DOCX/PPTX는 EMU → cm, HWPX는 HU → cm 자동 환산 (1자리 반올림).
169
+
137
170
  확장자로 자동 분기되므로 `.pptx` / `.hwpx`도 동일한 API를 사용합니다.
138
171
 
139
172
  ### 병합 셀 인지 동작 (v0.2+)
@@ -185,7 +218,7 @@ document-adapter-mcp
185
218
  }
186
219
  ```
187
220
 
188
- 재시작하면 Claude Desktop에서 아래 4개 도구를 사용할 수 있습니다.
221
+ 재시작하면 Claude Desktop에서 아래 7개 도구를 사용할 수 있습니다.
189
222
 
190
223
  ### Claude Code 설정
191
224
 
@@ -227,12 +260,13 @@ resp = client.messages.create(
227
260
 
228
261
  | 도구 | 설명 |
229
262
  |---|---|
230
- | `inspect_document` | 문서 구조(placeholders, tables)를 JSON으로 반환. **항상 첫 호출로 사용** |
263
+ | `inspect_document` | 문서 구조(placeholders, tables + `column_widths_cm`/`row_heights_cm`)를 JSON으로 반환. **항상 첫 호출로 사용** |
231
264
  | `render_template` | `{{key}}`를 context dict 값으로 치환해 새 파일 저장 |
232
- | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 반환 (preview의 40자 잘림 없이) |
265
+ | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 + `width_cm`/`height_cm`/`char_count` 반환 |
233
266
  | `set_cell` | 특정 표의 `(row, col)` 셀 값 교체 (병합 anchor만) |
234
267
  | `append_to_cell` | 기존 텍스트 뒤에 값 덧붙임 (라벨 유지용, 예: `"성 명"` → `"성 명 홍길동"`) |
235
- | `append_row` | 표 끝에 새 행 추가 (DOCX/HWPX 지원) |
268
+ | `fill_form` (v0.7+) | **라벨 이름**으로 일괄 채우기. 좌표 계산 없이 `{"접수번호": "...", "성명": "..."}` dict. dot-path 섹션 해소 지원 |
269
+ | `append_row` | 표 끝에 새 행 추가 (DOCX/PPTX/HWPX 전부 지원, v0.5+) |
236
270
 
237
271
  ### `inspect_document` 반환 예시 (v0.2+)
238
272
 
@@ -294,13 +328,12 @@ LLM은 이 preview를 보고 **"빈 셀이 어디 있는지 / 어떤 값을 넣
294
328
 
295
329
  loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `run`으로 쪼개질 수 있어, 어댑터가 paragraph 전체 텍스트를 재조립한 뒤 첫 `run`에 다시 담는 방식으로 처리합니다 (서식 일부 손실 가능).
296
330
 
297
- ## 내장된 버그 회피
331
+ ## 내장된 버그 회피 / 백엔드 선택
298
332
 
299
333
  | 포맷 | 문제 | 어댑터의 처리 |
300
334
  |---|---|---|
301
- | HWPX | `python-hwpx 2.9.0`의 `set_cell_text()`가 빈 셀에서 lxml/ElementTree 혼용 `TypeError` 발생 | `paragraphs[0].text = value` 직접 할당으로 우회 |
302
- | HWPX | `replace_text_in_runs()`가 한글 공백이 run으로 쪼개진 경우 매칭 실패 | 위치 기반 API만 사용 |
303
- | HWPX | `manifest fallback` 경고 로그가 과도하게 출력됨 | `logging.getLogger("hwpx")` 레벨을 `ERROR`로 조정 |
335
+ | HWPX | `python-hwpx` 가 Non-Commercial License → 상용 배포 블로커 | **v0.4.0 부터 자체 `hwpx_core` 모듈** (zipfile + lxml) 로 교체. 런타임에 `python-hwpx` 불필요. 테스트 fixture 생성에만 사용 (dev extras) |
336
+ | PPTX | `python-pptx` 에 공식 `add_row` API 없음 (issue #86, 2014년부터 open) | **v0.5.0 부터 자체 lxml 구현** (`<a:tr>` deepcopy 패턴) |
304
337
  | PPTX | placeholder가 여러 `run`으로 쪼개져 단순 `run.text` 치환이 실패 | paragraph 전체 재조립 |
305
338
  | DOCX | `docxtpl`의 `{%tr%}`를 같은 행에 두면 파싱 에러 | README에 배치 규칙 명시 |
306
339
 
@@ -309,15 +342,20 @@ loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `
309
342
  ```
310
343
  document_adapter/
311
344
  ├── __init__.py # load() dispatcher
312
- ├── base.py # DocumentAdapter ABC, TableSchema, DocumentSchema
345
+ ├── base.py # DocumentAdapter ABC + fill_form + dataclasses
313
346
  ├── docx_adapter.py # DocxAdapter
314
- ├── pptx_adapter.py # PptxAdapter
315
- ├── hwpx_adapter.py # HwpxAdapter (버그 회피 포함)
316
- ├── tools.py # Tool 정의 + call_tool dispatcher
347
+ ├── pptx_adapter.py # PptxAdapter (append_row 자체 구현 포함)
348
+ ├── hwpx_adapter.py # HwpxAdapter (hwpx_core 기반)
349
+ ├── hwpx_core/ # 자체 HWPX 패키지 (v0.4+)
350
+ │ ├── constants.py
351
+ │ ├── package.py # ZIP + dirty XML 관리
352
+ │ ├── grid.py # iter_grid, table_shape
353
+ │ └── paragraph.py # run-level 편집 헬퍼
354
+ ├── tools.py # 7개 MCP 도구 정의 + call_tool dispatcher
317
355
  └── mcp_server.py # MCP stdio server
318
356
 
319
357
  examples/
320
- └── claude_api_example.py
358
+ └── claude_api_example.py # Claude API Tool Use 에이전트 루프
321
359
  ```
322
360
 
323
361
  ## 라이선스
@@ -326,8 +364,15 @@ MIT
326
364
 
327
365
  ## Credits
328
366
 
329
- - [`python-docx`](https://github.com/python-openxml/python-docx)
330
- - [`docxtpl`](https://github.com/elapouya/python-docx-template)
331
- - [`python-pptx`](https://github.com/scanny/python-pptx)
332
- - [`python-hwpx`](https://github.com/airmang/python-hwpx)
333
- - [`mcp`](https://github.com/modelcontextprotocol/python-sdk)
367
+ **런타임 의존성** (전부 허용형 OSS):
368
+ - [`python-docx`](https://github.com/python-openxml/python-docx) — MIT
369
+ - [`docxtpl`](https://github.com/elapouya/python-docx-template) — LGPL-2.1
370
+ - [`python-pptx`](https://github.com/scanny/python-pptx) — MIT
371
+ - [`lxml`](https://lxml.de/) — BSD
372
+ - [`mcp`](https://github.com/modelcontextprotocol/python-sdk) — MIT
373
+
374
+ **코드 참조**:
375
+ - [`xgen-doc2chunk`](https://github.com/PlateerLab/xgen-doc2chunk) (Apache-2.0) — HWPX table grid 파싱 로직 차용 (`NOTICE` 참조)
376
+
377
+ **Dev 전용** (fixture 생성에만 사용):
378
+ - [`python-hwpx`](https://github.com/airmang/python-hwpx) — Non-Commercial License (v0.4.0 부터 런타임 의존성 제거)
@@ -86,13 +86,46 @@ doc.append_to_cell(table_index=2, row=0, col=0, value="홍길동")
86
86
  cell = doc.get_cell(table_index=1, row=3, col=2)
87
87
  print(cell.text, cell.is_anchor, cell.span, cell.nested_table_indices)
88
88
 
89
- # DOCX/HWPX는 행 추가 지원
89
+ # DOCX/PPTX/HWPX 전부 행 추가 지원 (v0.5+)
90
90
  doc.append_row(1, ["새 항목", "값"])
91
91
 
92
92
  doc.save("checklist_filled.docx")
93
93
  doc.close()
94
94
  ```
95
95
 
96
+ ### 라벨 기반 일괄 채우기 (v0.7+)
97
+
98
+ LLM 이 좌표 `(table_index, row, col)` 를 직접 계산하지 않고 "접수번호", "성명" 같은 사람이 읽는 라벨 key-value 로 양식을 채울 수 있습니다.
99
+
100
+ ```python
101
+ doc = load("form.hwpx")
102
+ result = doc.fill_form({
103
+ "접수번호": "2026-0001",
104
+ "성 명": "홍길동",
105
+ "주 소": "서울시 강남구",
106
+ "금융회사": "국민은행",
107
+ })
108
+ # → {"filled": [...], "not_found": [...], "ambiguous": [...]}
109
+ doc.save()
110
+ doc.close()
111
+ ```
112
+
113
+ - `auto` (기본): 라벨 셀 오른쪽 → 아래 → 같은 셀 순으로 값 셀 탐색. 보수적이라 기존 값 있는 셀은 다른 라벨로 간주하고 skip.
114
+ - `direction="right"` 명시: 라벨 오른쪽 셀을 **덮어쓰기** (예시값 있는 PPTX 템플릿 등).
115
+ - **Dot-path 섹션 지정**: 동일 라벨이 여러 섹션에 있으면 `"피해자.금액"`, `"지급정지요청계좌.금액"` 처럼 섹션 힌트 부여. `ambiguous` 반환 시 `hint` 필드에 예시 제공.
116
+ - **팁**: 한 양식의 관련 라벨을 한 번에 dict 로 넘기면 라벨끼리 서로 보호되어 오염을 방지합니다.
117
+
118
+ ### 셀 크기 메타 (v0.6+)
119
+
120
+ `get_tables()`가 `column_widths_cm` / `row_heights_cm` 를, `get_cell()`이 `width_cm` / `height_cm` / `char_count` 를 반환해 LLM이 **좁은 셀에 긴 텍스트를 넣어 오버플로 되는 것을 사전에 판단**할 수 있습니다.
121
+
122
+ ```python
123
+ cell = doc.get_cell(table_index=0, row=0, col=0)
124
+ print(cell.width_cm, cell.char_count) # 1.7cm, 4자 — 작은 배지
125
+ ```
126
+
127
+ DOCX/PPTX는 EMU → cm, HWPX는 HU → cm 자동 환산 (1자리 반올림).
128
+
96
129
  확장자로 자동 분기되므로 `.pptx` / `.hwpx`도 동일한 API를 사용합니다.
97
130
 
98
131
  ### 병합 셀 인지 동작 (v0.2+)
@@ -144,7 +177,7 @@ document-adapter-mcp
144
177
  }
145
178
  ```
146
179
 
147
- 재시작하면 Claude Desktop에서 아래 4개 도구를 사용할 수 있습니다.
180
+ 재시작하면 Claude Desktop에서 아래 7개 도구를 사용할 수 있습니다.
148
181
 
149
182
  ### Claude Code 설정
150
183
 
@@ -186,12 +219,13 @@ resp = client.messages.create(
186
219
 
187
220
  | 도구 | 설명 |
188
221
  |---|---|
189
- | `inspect_document` | 문서 구조(placeholders, tables)를 JSON으로 반환. **항상 첫 호출로 사용** |
222
+ | `inspect_document` | 문서 구조(placeholders, tables + `column_widths_cm`/`row_heights_cm`)를 JSON으로 반환. **항상 첫 호출로 사용** |
190
223
  | `render_template` | `{{key}}`를 context dict 값으로 치환해 새 파일 저장 |
191
- | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 반환 (preview의 40자 잘림 없이) |
224
+ | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 + `width_cm`/`height_cm`/`char_count` 반환 |
192
225
  | `set_cell` | 특정 표의 `(row, col)` 셀 값 교체 (병합 anchor만) |
193
226
  | `append_to_cell` | 기존 텍스트 뒤에 값 덧붙임 (라벨 유지용, 예: `"성 명"` → `"성 명 홍길동"`) |
194
- | `append_row` | 표 끝에 새 행 추가 (DOCX/HWPX 지원) |
227
+ | `fill_form` (v0.7+) | **라벨 이름**으로 일괄 채우기. 좌표 계산 없이 `{"접수번호": "...", "성명": "..."}` dict. dot-path 섹션 해소 지원 |
228
+ | `append_row` | 표 끝에 새 행 추가 (DOCX/PPTX/HWPX 전부 지원, v0.5+) |
195
229
 
196
230
  ### `inspect_document` 반환 예시 (v0.2+)
197
231
 
@@ -253,13 +287,12 @@ LLM은 이 preview를 보고 **"빈 셀이 어디 있는지 / 어떤 값을 넣
253
287
 
254
288
  loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `run`으로 쪼개질 수 있어, 어댑터가 paragraph 전체 텍스트를 재조립한 뒤 첫 `run`에 다시 담는 방식으로 처리합니다 (서식 일부 손실 가능).
255
289
 
256
- ## 내장된 버그 회피
290
+ ## 내장된 버그 회피 / 백엔드 선택
257
291
 
258
292
  | 포맷 | 문제 | 어댑터의 처리 |
259
293
  |---|---|---|
260
- | HWPX | `python-hwpx 2.9.0`의 `set_cell_text()`가 빈 셀에서 lxml/ElementTree 혼용 `TypeError` 발생 | `paragraphs[0].text = value` 직접 할당으로 우회 |
261
- | HWPX | `replace_text_in_runs()`가 한글 공백이 run으로 쪼개진 경우 매칭 실패 | 위치 기반 API만 사용 |
262
- | HWPX | `manifest fallback` 경고 로그가 과도하게 출력됨 | `logging.getLogger("hwpx")` 레벨을 `ERROR`로 조정 |
294
+ | HWPX | `python-hwpx` 가 Non-Commercial License → 상용 배포 블로커 | **v0.4.0 부터 자체 `hwpx_core` 모듈** (zipfile + lxml) 로 교체. 런타임에 `python-hwpx` 불필요. 테스트 fixture 생성에만 사용 (dev extras) |
295
+ | PPTX | `python-pptx` 에 공식 `add_row` API 없음 (issue #86, 2014년부터 open) | **v0.5.0 부터 자체 lxml 구현** (`<a:tr>` deepcopy 패턴) |
263
296
  | PPTX | placeholder가 여러 `run`으로 쪼개져 단순 `run.text` 치환이 실패 | paragraph 전체 재조립 |
264
297
  | DOCX | `docxtpl`의 `{%tr%}`를 같은 행에 두면 파싱 에러 | README에 배치 규칙 명시 |
265
298
 
@@ -268,15 +301,20 @@ loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `
268
301
  ```
269
302
  document_adapter/
270
303
  ├── __init__.py # load() dispatcher
271
- ├── base.py # DocumentAdapter ABC, TableSchema, DocumentSchema
304
+ ├── base.py # DocumentAdapter ABC + fill_form + dataclasses
272
305
  ├── docx_adapter.py # DocxAdapter
273
- ├── pptx_adapter.py # PptxAdapter
274
- ├── hwpx_adapter.py # HwpxAdapter (버그 회피 포함)
275
- ├── tools.py # Tool 정의 + call_tool dispatcher
306
+ ├── pptx_adapter.py # PptxAdapter (append_row 자체 구현 포함)
307
+ ├── hwpx_adapter.py # HwpxAdapter (hwpx_core 기반)
308
+ ├── hwpx_core/ # 자체 HWPX 패키지 (v0.4+)
309
+ │ ├── constants.py
310
+ │ ├── package.py # ZIP + dirty XML 관리
311
+ │ ├── grid.py # iter_grid, table_shape
312
+ │ └── paragraph.py # run-level 편집 헬퍼
313
+ ├── tools.py # 7개 MCP 도구 정의 + call_tool dispatcher
276
314
  └── mcp_server.py # MCP stdio server
277
315
 
278
316
  examples/
279
- └── claude_api_example.py
317
+ └── claude_api_example.py # Claude API Tool Use 에이전트 루프
280
318
  ```
281
319
 
282
320
  ## 라이선스
@@ -285,8 +323,15 @@ MIT
285
323
 
286
324
  ## Credits
287
325
 
288
- - [`python-docx`](https://github.com/python-openxml/python-docx)
289
- - [`docxtpl`](https://github.com/elapouya/python-docx-template)
290
- - [`python-pptx`](https://github.com/scanny/python-pptx)
291
- - [`python-hwpx`](https://github.com/airmang/python-hwpx)
292
- - [`mcp`](https://github.com/modelcontextprotocol/python-sdk)
326
+ **런타임 의존성** (전부 허용형 OSS):
327
+ - [`python-docx`](https://github.com/python-openxml/python-docx) — MIT
328
+ - [`docxtpl`](https://github.com/elapouya/python-docx-template) — LGPL-2.1
329
+ - [`python-pptx`](https://github.com/scanny/python-pptx) — MIT
330
+ - [`lxml`](https://lxml.de/) — BSD
331
+ - [`mcp`](https://github.com/modelcontextprotocol/python-sdk) — MIT
332
+
333
+ **코드 참조**:
334
+ - [`xgen-doc2chunk`](https://github.com/PlateerLab/xgen-doc2chunk) (Apache-2.0) — HWPX table grid 파싱 로직 차용 (`NOTICE` 참조)
335
+
336
+ **Dev 전용** (fixture 생성에만 사용):
337
+ - [`python-hwpx`](https://github.com/airmang/python-hwpx) — Non-Commercial License (v0.4.0 부터 런타임 의존성 제거)
@@ -177,11 +177,25 @@ TOOL_DEFINITIONS: list[dict[str, Any]] = [
177
177
  "description": (
178
178
  "라벨 이름으로 값 셀을 자동 탐지해 **일괄 채우기**. 좌표 (table_index, row, col) "
179
179
  "계산 없이 '접수번호', '성명' 같은 라벨 key-value dict 로 양식 채움. "
180
- "auto 모드: 라벨 셀 오른쪽 → 아래 → 같은 셀 순으로 값 셀 탐색. "
181
- "오른쪽/아래 셀이 사용자 요청 라벨 중 하나이면 (서로 라벨 공간 보호) skip 후 다음 시도. "
182
- "같은 셀로 fallback 시 append_to_cell 로 라벨 뒤에 값 덧붙임. "
183
- "**팁**: 한 양식의 관련 라벨을 함께 넘기면 라벨끼리 서로 보호하여 덮어쓰기 방지. "
184
- "반환: {filled:[...], not_found:[...], ambiguous:[...]}."
180
+ "\n"
181
+ "**direction 선택 기준 (중요)**: "
182
+ "(a) `auto` (기본, 보수적): 기존 값이 있는 셀은 다른 라벨로 간주하고 skip → 같은 "
183
+ "셀에 append_to_cell. **값 셀이 비어있는 양식**에 적합 (HWPX 공공 서식 등). "
184
+ "(b) `right` / `below` (명시): 라벨 오른쪽/아래 셀을 **덮어쓰기**. **기존에 "
185
+ "예시값이 채워져 있는 양식**(PPTX 템플릿 등) 에는 반드시 direction='right' 명시. "
186
+ "\n"
187
+ "**Dot-path 섹션 지정**: 같은 라벨이 여러 섹션에 있어 ambiguous 로 반환되면 "
188
+ "`{'피해자.금액': '...', '지급정지요청계좌.금액': '...'}` 처럼 섹션힌트.라벨 "
189
+ "형태로 재호출. ambiguous 반환의 hint 필드에 예시 제공됨. "
190
+ "\n"
191
+ "**output_path**: 생략 시 **원본 파일에 덮어쓰기** (대부분의 경우 생략 권장). "
192
+ "다른 위치에 저장이 필요할 때만 지정. "
193
+ "\n"
194
+ "**팁**: 한 양식의 관련 라벨을 **한 번에 dict 로** 넘기면 라벨끼리 서로 보호되어 "
195
+ "인접 라벨 오염이 방지됩니다. "
196
+ "\n"
197
+ "반환: `{filled: [...], not_found: [...], ambiguous: [...]}`. "
198
+ "ambiguous candidates 각각에 `context` 필드 포함 (어느 섹션인지 확인)."
185
199
  ),
186
200
  "input_schema": {
187
201
  "type": "object",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: document-adapter
3
- Version: 0.7.1
3
+ Version: 0.7.2
4
4
  Summary: LLM-friendly document template editing (DOCX/PPTX/HWPX) with MCP server and Claude API tool-use support
5
5
  Author-email: Son Seongjun <sonsj97@plateer.com>
6
6
  License: MIT
@@ -127,13 +127,46 @@ doc.append_to_cell(table_index=2, row=0, col=0, value="홍길동")
127
127
  cell = doc.get_cell(table_index=1, row=3, col=2)
128
128
  print(cell.text, cell.is_anchor, cell.span, cell.nested_table_indices)
129
129
 
130
- # DOCX/HWPX는 행 추가 지원
130
+ # DOCX/PPTX/HWPX 전부 행 추가 지원 (v0.5+)
131
131
  doc.append_row(1, ["새 항목", "값"])
132
132
 
133
133
  doc.save("checklist_filled.docx")
134
134
  doc.close()
135
135
  ```
136
136
 
137
+ ### 라벨 기반 일괄 채우기 (v0.7+)
138
+
139
+ LLM 이 좌표 `(table_index, row, col)` 를 직접 계산하지 않고 "접수번호", "성명" 같은 사람이 읽는 라벨 key-value 로 양식을 채울 수 있습니다.
140
+
141
+ ```python
142
+ doc = load("form.hwpx")
143
+ result = doc.fill_form({
144
+ "접수번호": "2026-0001",
145
+ "성 명": "홍길동",
146
+ "주 소": "서울시 강남구",
147
+ "금융회사": "국민은행",
148
+ })
149
+ # → {"filled": [...], "not_found": [...], "ambiguous": [...]}
150
+ doc.save()
151
+ doc.close()
152
+ ```
153
+
154
+ - `auto` (기본): 라벨 셀 오른쪽 → 아래 → 같은 셀 순으로 값 셀 탐색. 보수적이라 기존 값 있는 셀은 다른 라벨로 간주하고 skip.
155
+ - `direction="right"` 명시: 라벨 오른쪽 셀을 **덮어쓰기** (예시값 있는 PPTX 템플릿 등).
156
+ - **Dot-path 섹션 지정**: 동일 라벨이 여러 섹션에 있으면 `"피해자.금액"`, `"지급정지요청계좌.금액"` 처럼 섹션 힌트 부여. `ambiguous` 반환 시 `hint` 필드에 예시 제공.
157
+ - **팁**: 한 양식의 관련 라벨을 한 번에 dict 로 넘기면 라벨끼리 서로 보호되어 오염을 방지합니다.
158
+
159
+ ### 셀 크기 메타 (v0.6+)
160
+
161
+ `get_tables()`가 `column_widths_cm` / `row_heights_cm` 를, `get_cell()`이 `width_cm` / `height_cm` / `char_count` 를 반환해 LLM이 **좁은 셀에 긴 텍스트를 넣어 오버플로 되는 것을 사전에 판단**할 수 있습니다.
162
+
163
+ ```python
164
+ cell = doc.get_cell(table_index=0, row=0, col=0)
165
+ print(cell.width_cm, cell.char_count) # 1.7cm, 4자 — 작은 배지
166
+ ```
167
+
168
+ DOCX/PPTX는 EMU → cm, HWPX는 HU → cm 자동 환산 (1자리 반올림).
169
+
137
170
  확장자로 자동 분기되므로 `.pptx` / `.hwpx`도 동일한 API를 사용합니다.
138
171
 
139
172
  ### 병합 셀 인지 동작 (v0.2+)
@@ -185,7 +218,7 @@ document-adapter-mcp
185
218
  }
186
219
  ```
187
220
 
188
- 재시작하면 Claude Desktop에서 아래 4개 도구를 사용할 수 있습니다.
221
+ 재시작하면 Claude Desktop에서 아래 7개 도구를 사용할 수 있습니다.
189
222
 
190
223
  ### Claude Code 설정
191
224
 
@@ -227,12 +260,13 @@ resp = client.messages.create(
227
260
 
228
261
  | 도구 | 설명 |
229
262
  |---|---|
230
- | `inspect_document` | 문서 구조(placeholders, tables)를 JSON으로 반환. **항상 첫 호출로 사용** |
263
+ | `inspect_document` | 문서 구조(placeholders, tables + `column_widths_cm`/`row_heights_cm`)를 JSON으로 반환. **항상 첫 호출로 사용** |
231
264
  | `render_template` | `{{key}}`를 context dict 값으로 치환해 새 파일 저장 |
232
- | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 반환 (preview의 40자 잘림 없이) |
265
+ | `get_cell` | 셀 전체 텍스트 + 병합/중첩 메타 + `width_cm`/`height_cm`/`char_count` 반환 |
233
266
  | `set_cell` | 특정 표의 `(row, col)` 셀 값 교체 (병합 anchor만) |
234
267
  | `append_to_cell` | 기존 텍스트 뒤에 값 덧붙임 (라벨 유지용, 예: `"성 명"` → `"성 명 홍길동"`) |
235
- | `append_row` | 표 끝에 새 행 추가 (DOCX/HWPX 지원) |
268
+ | `fill_form` (v0.7+) | **라벨 이름**으로 일괄 채우기. 좌표 계산 없이 `{"접수번호": "...", "성명": "..."}` dict. dot-path 섹션 해소 지원 |
269
+ | `append_row` | 표 끝에 새 행 추가 (DOCX/PPTX/HWPX 전부 지원, v0.5+) |
236
270
 
237
271
  ### `inspect_document` 반환 예시 (v0.2+)
238
272
 
@@ -294,13 +328,12 @@ LLM은 이 preview를 보고 **"빈 셀이 어디 있는지 / 어떤 값을 넣
294
328
 
295
329
  loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `run`으로 쪼개질 수 있어, 어댑터가 paragraph 전체 텍스트를 재조립한 뒤 첫 `run`에 다시 담는 방식으로 처리합니다 (서식 일부 손실 가능).
296
330
 
297
- ## 내장된 버그 회피
331
+ ## 내장된 버그 회피 / 백엔드 선택
298
332
 
299
333
  | 포맷 | 문제 | 어댑터의 처리 |
300
334
  |---|---|---|
301
- | HWPX | `python-hwpx 2.9.0`의 `set_cell_text()`가 빈 셀에서 lxml/ElementTree 혼용 `TypeError` 발생 | `paragraphs[0].text = value` 직접 할당으로 우회 |
302
- | HWPX | `replace_text_in_runs()`가 한글 공백이 run으로 쪼개진 경우 매칭 실패 | 위치 기반 API만 사용 |
303
- | HWPX | `manifest fallback` 경고 로그가 과도하게 출력됨 | `logging.getLogger("hwpx")` 레벨을 `ERROR`로 조정 |
335
+ | HWPX | `python-hwpx` 가 Non-Commercial License → 상용 배포 블로커 | **v0.4.0 부터 자체 `hwpx_core` 모듈** (zipfile + lxml) 로 교체. 런타임에 `python-hwpx` 불필요. 테스트 fixture 생성에만 사용 (dev extras) |
336
+ | PPTX | `python-pptx` 에 공식 `add_row` API 없음 (issue #86, 2014년부터 open) | **v0.5.0 부터 자체 lxml 구현** (`<a:tr>` deepcopy 패턴) |
304
337
  | PPTX | placeholder가 여러 `run`으로 쪼개져 단순 `run.text` 치환이 실패 | paragraph 전체 재조립 |
305
338
  | DOCX | `docxtpl`의 `{%tr%}`를 같은 행에 두면 파싱 에러 | README에 배치 규칙 명시 |
306
339
 
@@ -309,15 +342,20 @@ loop / if / filter는 지원하지 않습니다. PPTX는 placeholder가 여러 `
309
342
  ```
310
343
  document_adapter/
311
344
  ├── __init__.py # load() dispatcher
312
- ├── base.py # DocumentAdapter ABC, TableSchema, DocumentSchema
345
+ ├── base.py # DocumentAdapter ABC + fill_form + dataclasses
313
346
  ├── docx_adapter.py # DocxAdapter
314
- ├── pptx_adapter.py # PptxAdapter
315
- ├── hwpx_adapter.py # HwpxAdapter (버그 회피 포함)
316
- ├── tools.py # Tool 정의 + call_tool dispatcher
347
+ ├── pptx_adapter.py # PptxAdapter (append_row 자체 구현 포함)
348
+ ├── hwpx_adapter.py # HwpxAdapter (hwpx_core 기반)
349
+ ├── hwpx_core/ # 자체 HWPX 패키지 (v0.4+)
350
+ │ ├── constants.py
351
+ │ ├── package.py # ZIP + dirty XML 관리
352
+ │ ├── grid.py # iter_grid, table_shape
353
+ │ └── paragraph.py # run-level 편집 헬퍼
354
+ ├── tools.py # 7개 MCP 도구 정의 + call_tool dispatcher
317
355
  └── mcp_server.py # MCP stdio server
318
356
 
319
357
  examples/
320
- └── claude_api_example.py
358
+ └── claude_api_example.py # Claude API Tool Use 에이전트 루프
321
359
  ```
322
360
 
323
361
  ## 라이선스
@@ -326,8 +364,15 @@ MIT
326
364
 
327
365
  ## Credits
328
366
 
329
- - [`python-docx`](https://github.com/python-openxml/python-docx)
330
- - [`docxtpl`](https://github.com/elapouya/python-docx-template)
331
- - [`python-pptx`](https://github.com/scanny/python-pptx)
332
- - [`python-hwpx`](https://github.com/airmang/python-hwpx)
333
- - [`mcp`](https://github.com/modelcontextprotocol/python-sdk)
367
+ **런타임 의존성** (전부 허용형 OSS):
368
+ - [`python-docx`](https://github.com/python-openxml/python-docx) — MIT
369
+ - [`docxtpl`](https://github.com/elapouya/python-docx-template) — LGPL-2.1
370
+ - [`python-pptx`](https://github.com/scanny/python-pptx) — MIT
371
+ - [`lxml`](https://lxml.de/) — BSD
372
+ - [`mcp`](https://github.com/modelcontextprotocol/python-sdk) — MIT
373
+
374
+ **코드 참조**:
375
+ - [`xgen-doc2chunk`](https://github.com/PlateerLab/xgen-doc2chunk) (Apache-2.0) — HWPX table grid 파싱 로직 차용 (`NOTICE` 참조)
376
+
377
+ **Dev 전용** (fixture 생성에만 사용):
378
+ - [`python-hwpx`](https://github.com/airmang/python-hwpx) — Non-Commercial License (v0.4.0 부터 런타임 의존성 제거)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "document-adapter"
7
- version = "0.7.1"
7
+ version = "0.7.2"
8
8
  description = "LLM-friendly document template editing (DOCX/PPTX/HWPX) with MCP server and Claude API tool-use support"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"