# 지원 매트릭스 (Support Matrix)
능력 영역별로 `python-hwpx` 코어가 실제로 무엇을 하는지, 그리고 그 상태가 어떤
증거에 근거하는지를 정리한다. 단순 "지원/미지원" 대신 아래 등급 어휘를 쓴다.
| 등급 | 의미 |
|---|---|
| **Parse** | 해당 요소를 읽어 구조로 노출한다. |
| **Preserve** | 손대지 않은 요소를 저장 시 바이트 그대로 보존한다(patch 경로). |
| **Edit** | 기존 요소를 편집한다. |
| **Create** | 새 요소를 밑바닥부터 생성한다. |
| **Render-verified** | 산출물이 실제 한컴 렌더 오라클로 검증됐다. |
| **Unsupported-but-preserved** | 생성·편집은 미지원이나, 기존 요소는 patch 저장 시 보존된다. |
| **Unsupported-and-rejected** | 미지원이며 입력 시 무음 처리 없이 예외로 거부한다(fail-closed). |
> **증거 축 주석.** 아래 수치는 전부 *생성물 수용률* 계열이며(동결 코퍼스 v2,
> 2026-07-19, 실한컴 12.0.0.3288 COM/GUI 오라클), 파서 프로젝트의 *파싱 recall*과는
> 다른 축이다. 상세는 [실측 코퍼스 메트릭](corpus-metrics.md) 참조.
## 매트릭스
| 능력 영역 | 상태 | 증거 |
|---|---|---|
| 문단·표 저작/편집 | Parse·Preserve·Edit·Create·Render-verified | corpus-metrics「오픈 수용률」476/476, 「저작 품질 게이트」실저작 58/58, 「렌더 검증」416건. **6.5+**: `paragraph.add_run(text, expand_special_characters=True)`가 `hp:lineBreak`(줄바꿈)·`hp:nbSpace`(비분리공백)·`hp:fwSpace`(전각공백)를 저작한다 — 실코퍼스 3파일(`error__20230818__test.hwpx`/`error__20251107__test.hwpx`/`error__20250808__...hwpx`) 리버스로 셋 다 **단일 hp:t 안의 mixed content**임을 확인(`hp:tab`이 쓰는 "hp:run 형제" 형태와 다름 — 스키마는 둘 다 허용하나 실 산출물은 이 셋에 대해 전자만 씀). 같은 리버스 과정에서 텍스트 추출기(`TextExtractor`)가 이 세 원자(+`hp:hyphen`)를 `hp:t` 중첩 위치에서 못 읽던 결함도 함께 수리 — 수리 전엔 원자가 조용히 사라지며 앞뒤 텍스트가 구분자 없이 붙어 읽혔다(줄바꿈이 있었다는 사실 자체가 유실). 실한컴 렌더 검증: `docs/openrate/report-v9.json` authored-inlineatoms 스트라텀(3원자의 0공백 순서조합 15종 전량), macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-07 측정). **6.12 트레인㊸ 갭④**: `doc.styles.apply_paragraph_format(column_break=...)`가 `hp:p`의 `columnBreak` 속성(문단 인스턴스별 강제 단 나눔)을 저작한다 — `page_break_before`가 다루는 `hh:breakSetting`(paraPr 공유 스타일)과는 다른 메커니즘이라 대상 paraPr을 새로 안 만들고 문단에 직접 적용된다. 실코퍼스 67파일 전수(14266문단) 측정: 자매 속성 `pageBreak`는 73건 실사용 확인(항상 "0"/"1" 리터럴, "true"/"false" 0건)되지만 `columnBreak`는 0건 — 그래도 같은 요소·같은 리터럴 규약을 재사용하는 것뿐이라 구조적 추측 위험은 없다. 실한컴 렌더 검증: `docs/openrate/report-v16.json` authored-column-break 스트라텀(5문단 문서에서 대상 문단 위치를 0~4로 로테이션, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-10 측정). 옛 5.x 표면(`doc.set_paragraph_format`)에는 의도적으로 안 얹었다 — 그 shim은 5.x 시그니처 그대로 얼려두는 게 계약이라(7.0 제거 예정), 신규 옵션은 `doc.styles.apply_paragraph_format`에만 산다 |
| 표 구조 변경(행·열·표 삭제/삽입, 열 오토핏) | Preserve·Edit·Render-verified | `hwpx.table_patch.apply_table_ops`; corpus-metrics「바이트 보존」497/497(patch 경로). 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-tablestructure 스트라텀(delete_column/delete_row/insert_row_by_clone/set_column_widths/autofit_columns 각 1건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정). **원장(coverage-ledger) 라우팅은 없음** — `apply_table_ops`가 건드리는 `hp:tbl`/`tr`/`tc`/`cellAddr`/`cellSpan`/`cellSz`/`cellMargin`은 전부 "문단·표 저작/편집"(대형 혼합 영역)에 이미 등록돼 있어, 여기 추가하면 이 스트라텀이 실제로 검증한 적 없는 이웃 요소까지 openrate 근거를 얻는 위양성이 생긴다(v8 polygon/arc 전례와 동일 판단) — 등급 갱신은 이 산문과 report-v14.json 직접 인용으로만. **6.12 트레인㊸ 갭⑤**가 `split_table`(표 나누기)·`merge_table`(표 붙이기) 두 op를 추가했다 — 스키마에 표 나누기/붙이기 전용 어휘는 없다(`hp:tbl`/`TableType`은 `pageBreak`/`repeatHeader`/`rowCnt`/`colCnt`뿐, `ParaList XML schema.xml:2008`), 순수 구조 편집이라 `_insert_block_by_clone`과 같은 fail-closed 원칙(병합 셀이 경계를 걸치면 거부)을 그대로 따른다. `split_table`은 `split_row` 물리 행 인덱스에서 표를 둘로 나눠(윗칸은 원래 표 id·아랫칸은 새 래핑 문단+새 표 id) 병합 셀이 경계를 걸치면 거부한다(실코퍼스 `public_official_table.hwpx`로 clean·crossing 양쪽 검증). `merge_table`은 그 역연산 — 바로 다음 표와 합치되 `colCnt` 불일치나 두 표 사이 실텍스트 존재 시 거부(내용 무음 소실 방지). **"표 뒤집기"는 이번 트레인에서 의도적으로 보류**: 행/열 순서 반전은 병합 셀·중첩표에서 정의가 흐려지고(예: rowSpan 셀을 반전하면 어느 칸이 내용을 갖는가?) 실코퍼스에 근거로 삼을 예시가 전혀 없다(스키마조차 이런 연산의 존재를 암시하지 않는다) — curve·connectLine과 같은 원칙으로 근거 없는 알고리즘을 추측하지 않는다. 실한컴 렌더 검증: `docs/openrate/report-v16.json` authored-table-split-merge 스트라텀(분할 3종[3/4/5행 표를 각기 다른 위치에서 분할]·병합 1건[2+2행 표 결합]·분할 후 재병합 왕복 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-10 측정, 기존 v14의 15/15은 split/merge를 겨냥하지 않았었다) |
| 표 생성(병합·중첩 포함) | Create·Render-verified | `add_table`·`merge_cells`(피복 셀 제거+cellSpan — 실한컴 병합 의미론과 동형, 3종 병합 렌더 픽셀 확인)·셀 문단 `add_table`(중첩). 기본값은 5.4.0에서 실한컴에 맞췄다: 셀 안여백 510/510/141/141, 기본 표 폭은 본문 폭, 중첩 표는 부모 셀 사용 폭. **남은 갭**: 선 종류는 raw 전용. **6.7+**: `table.label`/`.set_label()`/`.remove_label()`(`hp:label`, Avery형 라벨시트/명패 인쇄 레이아웃) — 사설 코퍼스 75건·436건 리버스(DEV-023, 스키마와 완전 일치), 항상 표의 마지막 자식으로 배치. **Render-verified(6.8, 트레인㉘)**: openrate v11 실한컴 배치(macOS GUI 오라클) 15/15 render-checked·음성대조 3/3 — 관측 2클러스터(2×9 소형시트·1×2 대형 정사각 명패)와 관측 밖 조합(3×6 미관측 그리드·landscape=NARROWLY·partial 속성) 전부 수용, 수용 경계가 관측 분포보다 넓다. **6.13 트레인㊻**: `equalize_column_widths()`/`equalize_row_heights()`(`HwpxOxmlTable`) — 편집기 메뉴 표면 역매핑(트레인㊷·㊺)이 찾은 부분 대응(균등화 "전용" 헬퍼 부재)을 닫는다. 신규 XML 어휘는 전혀 없음: 전자는 기존 `set_column_widths([1]*column_count)`를 전용 이름으로 노출할 뿐이고, 후자는 그 행 대응(같은 `iter_grid`/rowSpan 로직·`_distribute_size` 재사용, 병합 셀은 자신이 덮는 행/열들의 새 크기 합을 받는다 — `cellSz`만 갱신). 실한컴 렌더 검증: `docs/openrate/report-v17.json` authored-cell-equalize 스트라텀(열만/행만/양축/양축+병합 셀 1개 로테이션, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-11 측정) — 기존 `set_column_widths` 자체는 v14의 authored-tablestructure에서 1건 확인됐을 뿐 이 두 신규 편의 메서드 자체는 이번이 첫 독립 검증 |
| 양식 채움(byte-splice) | Preserve·Edit·Render-verified | `hwpx.patch`·`table_patch`·`body_patch`; corpus-metrics「바이트 보존」497/497. wild 공개 양식의 서식 충실은 2차 실측 후 **산출분 pass 82.1%(23/28)·산출+fail 7.6%(5/66)** — 불가능 타깃은 typed 거부 35건(전수 감별 과잉거부 0). 잔존은 페이지 리플 5건(4건 typed 경고 동반·완전 무음 1건, 「구조결함 2차 실측」절), 표 shape 부류는 측정 인공물로 판명·판정기 교정. **6.11 사이클(트레인㊳ 판정, ㊵ 이후 정리 트레인 생성)**: 이 영역 자체의 render-verified 갭(v14 소탕 대상에서 빠뜨림으로 판정)을 v15로 메움 — `docs/openrate/report-v15.json` authored-formfill 스트라텀(합성 2열 양식 → `table_patch.fill_cells`로 빈 값 셀 byte-splice 채움, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정). **원장 라우팅은 없음** — `fill_cells`가 건드리는 `hp:t`는 기존 요소 텍스트 치환일 뿐 새 요소를 안 만든다(find-replace·document-merge와 같은 이유) — 등급 갱신은 이 산문과 report-v15.json 직접 인용으로만 |
| 편집 계획 실행(edit plan) | Preserve·Edit·Render-verified | `hwpx.plan.apply_edit_plan`(experimental, 5.6.0+): 바이트-스플라이스 op 7종을 계획 1파일로 합성 — 정적 선검증→전 체인 인메모리 실행→최종 open-safety 검증→단 1회 원자 쓰기. 중간 실패 시 output·source 바이트 불변(테스트가 바이트 비교로 증명), step별+원본→최종 `hwpx.mutation-report/v1` 실측 사영. 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-editplan 스트라텀(paragraph_patch/fill_cells/apply_table_ops(중첩)/recolor_runs_by_color/strip_runs_by_color 각 1건 — v3/v4의 옛 `edit-plan` 버킷이 이미 다룬 paragraph_patch+fill_cells 조합 이외의 3개 op가 이번 신규 실증분), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정). **원장 라우팅은 없음** — 이 모듈은 기존 op를 체이닝만 할 뿐 대응하는 단일 capabilityArea가 없다(formfield류 저수준 공유 요소와 같은 이유) — 등급 갱신은 이 산문과 report-v14.json 직접 인용으로만 |
| 도형 저작(선·사각형·타원) | Parse·Preserve·Edit·Create·Render-verified | 전용 `add_line`·`add_rectangle`·`add_ellipse`. 5.0.0에서 기하 네임스페이스(`hc:`)·선 bounding box·`resize()`의 기하 반영을 수정한 뒤 실한컴 12.30.0(build 6446)에서 **개봉 7/7**(선 수평·대각·수직, 사각형, 타원, resize 후 혼합, 그림). 렌더로 사각형 144×72pt+`#CCE5FF`, 타원 100×60pt+`#FFD9CC`, 대각선이 요청 박스를 실제 span, `resize(14400,7200)` 후 새 크기·DASH 반영까지 확인 |
| 저수준 도형·컨트롤 탈출구 | Edit | `add_shape`·`add_control`은 건네받은 요소와 속성만 쓰고 OWPML 필수 하위 요소(`offset`·`orgSz`·`curSz`·`sz`·`pos`·유형별 기하)는 만들지 않는다. 그대로 저장한 문서는 실한컴 12.30.0이 **거부**한다(음성 대조로 확인). 생성 시점의 `UserWarning`은 여전히 하나뿐이지만(휘발성 — 호출 스택에만 남고 파일엔 안 남음), **6.6 트레인㉒**부터 `validate_package`·`validate_editor_open_safety`가 이 위험을 저장 이후에도 검증 결과에 명시적 `warning`으로 남긴다(fail-closed 아님 — `ok`는 그대로 `True`, 정직한 신호만 추가). `hp:container` 부재는 오탐 안 됨(부재는 이 5종을 원래 안 가짐, DEV-016). 도형은 위의 전용 헬퍼를 쓸 것(전용 헬퍼는 경고하지 않는다) |
| arc·polygon·curve·connectLine | Parse·Preserve·Create(arc·polygon, experimental)·Render-verified(arc·polygon만)·Unsupported-but-preserved(curve·connectLine) | **6.11 트레인㊳ 재발견**: `docs/openrate/report-v8.json`의 authored-polygon/authored-arc 스트라텀(각 15/15 render_checked·0 render_failed)이 애초부터 실재했고 원장(coverage-ledger)에도 `hp:polygon`/`hp:arc`로 이미 정확히 반영돼 있었다 — 하지만 이 행의 등급 문자열에 "Render-verified"가 한 번도 반영되지 않아 편집기 표면 인벤토리가 이 항목을 계속 "미실측"으로 잘못 보고해 왔다(순수 매트릭스 산문 갱신 누락, "차트" 행과 같은 종류의 결함, 이번 트레인에서 함께 바로잡음). curve·connectLine은 아래 서술대로 여전히 정직 보류(저작 API 자체가 없음, Render-verified 대상 아님). 전용 `add_polygon`(6.4+, `doc.shapes`): 실코퍼스(`hwpxlib_corpus/reader_writer__SimplePolygon.hwpx`) 리버스 — 꼭짓점은 `hc:pt`(core 네임스페이스, rect/ellipse와 같은 기하 네임스페이스 계약: 5.0.0 도형 네임스페이스 결함 수리와 동일 축), 자기 bbox 좌상단 원점 로컬 좌표계(정점 목록이 그 자기 `orgSz`와 정확히 일치, 실측). `resize()`는 이미 범용(`pt` 로컬명 스캔)이라 변경 없이 다각형에도 적용된다. 전용 `add_arc`(6.4+, `doc.shapes`): 실코퍼스(`SimpleArc.hwpx`) 리버스 — 스키마상 `hp:arc`는 좌표 3점(`center`/`ax1`/`ax2`)뿐 각도 필드가 아예 없다. 유일한 실 예시는 center가 자기 bbox 모서리에 앉고 ax1이 그 바로 아래, ax2가 그 바로 오른쪽인 사분원 하나뿐이라 그 한 배치만 점 단위로 실측 검증됐고, 나머지 세 모서리는 다른 모든 도형이 이미 쓰는 `hp:flip` 미러링으로 얻는다(새 점 계산 없음 — `corner` 인자, `TOP_LEFT`만 실측·나머지 3개는 유도). `arc_type`(NORMAL/PIE/CHORD)은 스키마 열거값 그대로 통과. **curve는 이번 사이클 정직 보류**: 유일한 실 예시(`SimpleCurve.hwpx`)에서 곡선의 `orgSz`가 꼭짓점(`hp:seg` 앵커점) bbox보다 뚜렷이 크다(폭 16636 vs 앵커점 bbox 15225·높이 21360 vs 20500) — 스플라인이 자기 앵커점 밖으로 부풀어 오르는 실측이며, 한컴의 정확한 곡선 적합 알고리즘 근거 없이 bbox를 추정하면 침묵 오류가 된다. **connectLine도 정직 보류**: 유일한 "Simple" 정본 예시(`SimpleConnectLine.hwpx`)는 자유선이 아니라 `subjectIDRef`로 다른 두 도형(rect·ellipse)을 잇는 "스마트 연결선"이고, `offset`이 음수(부호 없는 32비트로 직렬화된 것을 실측 확인)·`curSz`가 `orgSz`와 다르며·`scaMatrix`가 평행이동까지 얹은 비항등 행렬이라 그 관계식 근거가 없다. 다른 예시(`error__20230818__test.hwpx`, 미부착 `subjectIDRef=0`)는 파일명이 스스로 결함 사례임을 밝히고 `scaMatrix`가 e1=0인 퇴화 행렬이라 정본으로 못 쓴다. `startPt`/`endPt`가 `hp:connectLine`에서는 `hp:` 네임스페이스(`hp:line`의 `hc:startPt`와 다름, 실측 확인)라는 점만 확정 — 도형마다 기하 네임스페이스를 개별 검증해야 한다는 교훈 재확인 |
| 그룹 개체(컨테이너) | Parse·Create(experimental)·Render-verified | 전용 `add_container`(6.5+, `doc.shapes`): 실코퍼스(`hwpxlib_corpus/reader_writer__SimpleContainer.hwpx` 3부재 + 실문서 2종 71개 컨테이너, 총 74개 표본) 리버스 — 부재는 독립 도형과 완전히 같은 구조(offset/orgSz/curSz/flip/rotationInfo/renderingInfo + 도형별 기하)를 유지하되 `AbstractShapeObjectType` 꼬리(sz/pos/outMargin/shapeComment — 그룹만 가짐)는 없고 `groupLevel="1"`(그룹 자신은 `"0"`), `renderingInfo`의 `transMatrix` 이동성분이 자기 `offset`과 일치, `numberingType="PICTURE"` 전량 관측. `ContainerMember.rect`/`.ellipse`/`.polygon`으로 부재를 그룹 로컬 좌표에 배치 — **`pic`/`arc`/`line`/`connectLine`/중첩 `container` 부재와 이미 배치된 도형의 재그룹은 이번 사이클 범위 밖**(전자는 같은 패턴으로 자연 확장 가능, `pic`은 media 배선이 추가로 필요). **알려진 한계**: `resize()`는 컨테이너 자신의 크기만 갱신하고 부재를 비례 재배치하지 않는다(직속 자식 스캔이 부재 도형 전체를 보지, 그 안의 점 좌표를 안 봄) — 부재별로 개별 `resize()`를 부를 것. 실한컴 렌더 검증: `docs/openrate/report-v9.json` authored-container 스트라텀(2~4부재·rect/ellipse/polygon 슬롯별 회전), macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-07 측정) |
| 그림 삽입/치환 | Edit·Create·Render-verified | `add_picture`·`add_image`·`doc.media.replace_picture`. 단순 그림 개체 자동 생성은 지원하며 실한컴 개봉을 확인했다(실한컴 코퍼스와 자식 요소 단위 대조). 효과 있는 복잡 개체 생성은 미지원. 그림을 포함한 그룹 생성은 위 "그룹 개체(컨테이너)" 참조(현재 그림 부재는 미지원). 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-picture 스트라텀(크기 변주 4건 + `replace_picture` 자산 교체 1건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정) — `hp:pic`/`imgRect`/`imgDim`/`imgClip`/`effects`/`hc:img`는 `add_picture` 호출마다 무조건 방출되는 고정 스켈레톤이라(파라미터 유무 무관, 직접 확인) 이 영역째 원장 라우팅해도 위양성 없음 |
| 차트 | Create(experimental)·Preserve·Render-verified | `doc.shapes.add_chart`(5.3.0+): 데이터·계열·축 레이블에서 차트 개체를 생성한다. 기존 차트 part는 patch 저장 시 바이트 보존(497/497). 생성 어휘 밖의 차트 종류·서식은 미지원이며 기존 개체 보존으로만 다룬다. 실한컴 렌더 검증: `docs/openrate/report-v4.json` authored-chart 스트라텀(bar/line/pie, `hwpx_automation.office.charting.build_chart_ml`로 생성) 15/15 render_checked·0 render_failed — **이 v4 증거는 이미 존재했으나 "Render-verified" 등급이 이번(6.11 트레인㊳)까지 한 번도 이 행에 반영되지 않았다(순수 매트릭스 산문 갱신 누락, 재발견해서 바로잡음)**. `docs/openrate/report-v14.json` authored-chart 스트라텀(pie/bar/line 로테이션, core 저장소 자체 손빌드 최소 ChartML — automation 저장소 임포트 불가라 v4의 build_chart_ml을 재사용 못 함, 단방향 아키텍처) 5/5 render_checked·0 render_failed(2026-08-08 측정)로 두 번째 독립 배치 추가 — 같은 스트라텀명이라 원장이 두 report를 OR로 자동 누적 |
| 수식 | Parse·Create(experimental)·Render-verified | `add_equation`(EqEdit script 삽입, 5.2.0+)과 `hwpx.equation.latex_to_eqedit`(렌더 검증 토큰셋만 변환, 밖은 `UnsupportedLatexError`로 typed 거부). 저작 어휘 전 토큰을 실한컴 렌더 오라클 픽셀 실측(60수식 배터리)으로 확정했고, 기존 수식 개체는 파싱·patch 보존됨(미리보기 MathML 렌더는 뷰어/플러그인 계층) |
| 문단 첫 글자 장식(드롭캡) | Create(experimental)·Render-verified | `doc.shapes.add_drop_cap`(6.12 트레인㊸, 갭②) — 편집기 메뉴 표면 역매핑(트레인㊷)이 찾은 신규 갭. **어휘가 문단 속성이 아니라 도형 공통속성이라는 게 첫 발견**: `dropcapstyle`(`hc:DropCapStyleType`, `None`/`DoubleLine`/`TripleLine`/`Margin`)은 `hh:paraPr`가 아니라 `AbstractShapeObjectType`(rect/picture/table/chart 등 모든 삽입 개체가 공유하는 공통 속성군, `ParaList XML schema.xml:2006`)에 산다 — 이 라이브러리의 기존 도형 빌더 전부가 이 속성을 `"None"`으로 고정 방출해 온 이유가 이거다. 실코퍼스 67파일 전수에서 `dropcapstyle="TripleLine"` 실사용은 `error__20230809__test.hwpx` 단 1건뿐(나머지 전부 `"None"`) — 그 1건을 역설계: 드롭캡은 문단 첫 글자를 잘라내는 게 아니라 **투명한 `hp:rect`(선 `style="NONE"`·채움/그림자 `alpha="0"`, `lock="1"`)를 run에 끼워 넣고, 그 안 `drawText/subList/p/run/t`에 키운 문자를 별도로 담는** 구조다. 배치는 일반 도형 빌더의 "흐르지 않는 개체" 기본값(`horzRelTo="COLUMN"`·`flowWithText="0"`)과 달리 `horzRelTo="PARA"`·`flowWithText="1"`, `curSz`는 `orgSz`와 다르게 `"0"`/`"0"`(리사이즈 안 됨 센티널로 추정), `outMargin right="850"`(다른 셋은 0). 정체불명 `hp:parameterset`(`name="539"`, 값 `"2"`)은 DEV-011 선례대로 의미 추정 없이 그대로 보존. **v1 스코프는 `TripleLine`만**: 실 예시가 1건뿐이라 `DoubleLine`/`Margin`이 같은 rect+drawText 패턴인지 구조 자체가 다른지 검증 근거가 없어 typed 거부로 정직 보류(curve·connectLine과 같은 원칙). width/height도 실측된 폰트-크기→박스-크기 변환 공식이 없어 호출자가 직접 지정(자동 계산 시도 안 함). 원장 라우팅은 없음(`hp:rect`가 이미 "도형 저작" 영역에 등록돼 있어 재등록하면 그 카운트를 덮어씀, `hp:fieldBegin`류보다 더 확실한 공유 요소 사례). 실한컴 렌더 검증: `docs/openrate/report-v16.json` authored-drop-cap 스트라텀(문자 5종 로테이션 + width/height 변주 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-10 측정) — `TripleLine` 스코프 그대로, `DoubleLine`/`Margin`은 여전히 미저작(typed 거부) |
| 변경추적(redline) | Edit·Create·Render-verified | `doc.tracking.insert`/`.delete`/`.replace`(`add_tracked_insert`·`add_tracked_delete`·`add_tracked_replace`의 현재 이름); 실 Windows 한컴 COM `IsTrackChange=1`·검토 리본 수락/거부 스파이크. **렌더 관측 두 갈래(정직 병기, 미해소)**: corpus-metrics의 실사용 코퍼스(Windows COM `SaveAs("PDF")`, 기존 문서에 이미 담긴 실이력 변경추적)에서는 43건이 `render_unavailable`(한컴이 PDF export 자체를 거부)로 집계됐다 — 반면 `docs/openrate/report-v14.json` authored-redline 스트라텀(macOS Hancom GUI 오라클, `doc.tracking.insert`/`.delete`/`.replace`로 신규 저작한 단순 삽입/삭제/치환 각 1~2건, 단일 저자)은 5/5 render_checked·0 render_failed로 성공했다(2026-08-08 측정). 두 관측이 진짜 모순인지(예: Windows COM vs macOS GUI 경로 차이, 또는 실이력의 복잡도가 export 거부를 유발) 추가 조사 없이는 확정할 수 없다 — 신규 저작·단순 변경추적은 렌더 검증됨, 복잡한/기존 이력 변경추적의 export 거부 위험은 여전히 알려진 한계로 남긴다 |
| 메모(코멘트) | Edit·Create·Render-verified | `add_memo`·`add_memo_with_anchor`; subList 코멘트 텍스트 + `MemoShapeIDRef` 버그 수정을 실 Windows 한컴에서 검증(CHANGELOG). `doc.styles.ensure_memo_shape`(6.2+)로 `hh:memoPr` 모양 정의(선 두께·색·채움색·활성색)를 새로 만들어 `add_memo(memo_shape_id_ref=)`로 바로 연결 — 이전엔 기존 문서의 memoPr에만 의존했다. 기본값은 실코퍼스(hwpxlib_corpus, 6파일) 최빈 프로파일 |
| 각주/미주 | Edit·Create·Render-verified | `add_footnote`·`add_endnote`; M6 읽기 경로에서 note 노출. 5.5.0에서 실한컴 gold 계약으로 방출 수리(본문 run 내 `hp:ctrl` 래핑·`number`/`suffixChar`·각주 본문 `autoNum`+스타일 15/16) — 옛 방출이 각주를 그리지 않던 실결함 종결, 리더는 실한컴산·구식 양형상 모두 수용(CHANGELOG [5.5.0]) |
| 네이티브 목차(TOC)/상호참조 | Create·Render-verified | `hwpx.tools.toc_author.add_native_toc`·`mark_toc_dirty`·`toc_verify`; corpus-metrics「네이티브 목차」구조 15/15, 실한컴 재계산 후 페이지 정합 5/5 |
| 차례 숨기기·제목 차례 표시 | Create(experimental)·Render-verified | `HwpxOxmlParagraph.add_title_mark(in_toc=)`(6.15 트레인, DEV-044). 스키마는 `hp:titleMark`를 `hp:t`의 선택군 자식으로 실제 선언하되(`ignore: xs:boolean, default="false"`) `xs:documentation`이 전혀 없어 의미는 미문서화 — 실코퍼스 14건(전부 `ignore="1"`, 차례 페이지 항목 문단의 헤딩 텍스트 미러 run 바로 앞)으로 구조는 6.13에 이미 확정됐으나, 캐럿이 있는 문단에 정확히 들어간다는 타겟팅 계약은 이 환경의 자동화 한계(캔버스 클릭·키 입력 모두 문서에 미도달)로 실측이 안 돼 저작을 보류하고 있었다. **6.15**: 팀장의 Windows 박스 COM `SetPos`+`MarkTitle`/`HideTitle` 3변형(`SetPos(0,2,0)`+`MarkTitle`→p2에만 `ignore="1"`, `SetPos(0,2,0)`+`HideTitle`→p2에만 `ignore="0"`, `SetPos(0,1,0)`+`MarkTitle`→p1에만 `ignore="1"`)이 타겟팅을 확정 — 마크는 항상 캐럿이 있는 문단의 첫 run 첫 `hp:t` 맨 앞, 그 문단 자신의 텍스트 바로 앞에 들어간다(이전 Mac 프로브의 절 정의 문단 착지는 캐럿 이동 불가에 따른 퇴화 현상이었음이 확인됨). 폴라리티는 이름의 직관과 반대(`in_toc=True`="제목 차례 표시"→`ignore="1"`, `in_toc=False`="차례 숨기기"→`ignore="0"`) — Mac GUI A/B와 Windows COM 양쪽에서 독립 재확인. 호출자가 대상 문단을 직접 지정하는 API 형태가 실 편집기의 "캐럿 문단" 타겟팅과 정확히 대응한다. 실한컴 렌더 검증: `docs/openrate/report-v21.json` authored-title-mark 스트라텀(극성 로테이션·단일/두-표제 문서·양쪽 표제 반대 극성 동시 표시·기존 charPr 합성 각 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-15 측정) |
| 색인 표시 | Create(experimental) | `HwpxOxmlParagraph.add_index_mark(first, second=)`(P4). 팀장 실한컴(HWP 13.0.0.3901) GUI 프로브 gold 2건(`tests/fixtures/gui_probes/index_mark_first_only.hwpx`=1단계 키만, `index_mark_two_keys.hwpx`=2단계 키까지) 역설계. 배치는 `add_title_mark`와 **다르다** — titleMark는 `hp:t` 안에 들어가지만 indexmark는 같은 run 안의 `hp:ctrl` 형제로 텍스트 **앞**에 선다(`appleapple item paragraph.`). 타겟팅 계약은 같다 — 호출자가 대상 문단을 직접 지정하는 형태가 실 편집기의 "캐럿이 있는 문단"과 대응한다. **스키마 편차**: `ParaList XML schema.xml:209-216`은 `firstKey`/`secondKey`를 둘 다 필수 시퀀스로 선언하는데(속성 0개, 두 키 모두 무타입) 실물은 1단계 색인에서 `secondKey`를 아예 생략한다 — `add_new_num`의 `autoNumFormat` 생략과 같은 부류로 실측을 따랐다(빈 문자열 키는 생략과 방출 XML이 달라져 typed 거부). 코퍼스 실사용 0건 — 계약 근거는 GUI 프로브 gold 전부. 실한컴 렌더 검증: `docs/openrate/report-v22.json` authored-index-mark 스트라텀(1키/2키/두 문단 독립 마크/**한 문단 2마크(관측 밖 경계)**/커스텀 charPr 표제 합성, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-18 측정) — 관측 밖 조합 포함 전량 수용 |
| 암호화 HWPX | Unsupported-and-rejected | 복호화 API 없음. 암호화된 content part는 파싱 단계에서 예외(`XMLSyntaxError`)로 거부 — 무음으로 잘못된 문서를 만들지 않음(fail-closed) |
| HWP 5.x 바이너리 | Unsupported-and-rejected | HWP v5는 ZIP이 아니므로 열기 시 `BadZipFile` 예외. OLE2/CFBF 시그니처를 확인하면 예외 메시지가 HWPX 변환을 안내한다(예외 타입은 그대로) |
| 누름틀(form field) 생성 | Parse·Edit·Create(experimental)·Render-verified | `list_form_fields`·`fill_form_field`로 조회·서식 보존 채움에 더해, `doc.fields.add`(구 `add_form_field`, 5.1.0+)가 실한컴 CLICKHERE 계약 그대로 신규 누름틀을 생성한다(표 셀 배치 포함). 만든 필드는 기존 list/fill과 실제 한컴이 특수분기 없이 소비. 실한컴 렌더 검증: `docs/openrate/report-v15.json` authored-formfield 스트라텀(prompt/memo/editable 로테이션 + 표 셀 배치 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정) — 이 영역의 첫 독립 실한컴 판정. **원장 라우팅은 없음** — `hp:fieldBegin`은 CLICKHERE/TOC/CROSSREF/하이퍼링크/메모가 공유하는 저수준 요소라 여전히 미등록(무근거 승격 회피 원칙) — 등급 갱신은 이 산문과 report-v15.json 직접 인용으로만 |
| 체크박스 양식개체 | Create·Render-verified | `add_check_box`·`list_check_boxes`·`set_check_box`(5.7.0+). 실한컴 실측 계약: `value` CHECKED=☑ / UNCHECKED=□, ``는 **필수 자식**(없으면 한컴이 문서를 거부하는데 우리 open-safety·ID 무결성은 통과한다 — 실한컴이 유일한 판정자다). 라디오(`hp:radioBtn`)·명령단추(`hp:btn`)는 읽기·보존만 하고 저작 API 없음 |
| 형광펜(하이라이트) | Parse·Create(experimental)·Render-verified | `doc.text.highlight`·`doc.text.highlights`(6.2+). 실코퍼스(`hwpxlib_corpus/error__20251107__test*.hwpx`)와 OWPML 스키마(`ParaList XML schema.xml`) 리버스: `markpenBegin`/`markpenEnd`는 단일 `hp:t` 안에서 위치로 짝짓는다(id 없음) — `add_tracked_delete`와 같은 단일-run 매치 제약을 그대로 따른다. 색은 `#RRGGBB` 6자리 16진만 허용(typed 거부). 실한컴 렌더 검증: `docs/openrate/report-v6.json` authored-highlight 스트라텀, macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-06 측정) |
| 테두리 채우기(이미지·그라데이션) | Parse·Create(experimental)·Render-verified | `doc.styles.ensure_border_fill(fill_image=/fill_gradient=)`·`HwpxOxmlTable.set_cell_fill_image`/`set_cell_fill_gradient`(6.2+). 실코퍼스(`hwpxlib_corpus`, imgBrush 5파일·gradation 2파일) 리버스: `hc:fillBrush`는 `winBrush`/`imgBrush`/`gradation` 중 하나만 허용하는 선택형(Core XML schema.xml:650) — 상호 배타 typed 거부. `fill_image`는 `doc.media.add_image`가 등록한 이진 항목을 `mode`(관측 전량 `TOTAL`)로 참조하고, `fill_gradient`는 색 목록(≥2)·`type`(관측 전량 `LINEAR`)·`angle` 등을 받는다. 실한컴 렌더 검증: `docs/openrate/report-v6.json` authored-fill 스트라텀, macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-06 측정) |
| 문서 정보(메타데이터) | Parse·Edit·Render-verified | `doc.parts.metadata`(읽기)·`.set_document_metadata`(쓰기, 6.12 트레인㊸) — `Contents/content.hpf`의 `opf:metadata`(제목/작성자/주제/키워드/작성일/수정일). 편집기 메뉴 표면 역매핑(트레인㊷)이 찾은 신규 갭("파일→문서 정보…") — `opf:metadata` 자체는 모든 실 문서에 실재하나(실코퍼스 67파일 확인) 이전엔 `opc/package.py`가 `opf:manifest`(부품 목록)만 다루고 `opf:metadata`는 전혀 안 건드렸다. 실코퍼스 67파일 전수 리버스: `opf:title`은 65/67 요소 존재(31건 빈 문자열) · `opf:language`는 항상 `"ko"` · `creator`/`subject`/`keyword`/`lastsaveby`/`description` 요소는 항상 있으나 값은 거의 항상 빈 문자열(subject/description/keyword는 65개 중 각 1건만 비어있지 않음) · **`CreatedDate`/`ModifiedDate`는 65/65 항상 존재·비지 않고 단일 포맷**(ISO 8601 `"%Y-%m-%dT%H:%M:%SZ"`)으로 100% 일관 — 저작 대상에 포함. **`date`(사람이 읽는 자유형식 날짜)는 저작 대상에서 제외**: 최소 5가지 서로 다른 포맷을 관측했다(현행 한컴의 `"YYYY년 M월 D일 요일 오전/오후 H:MM:SS"` 다수, 구버전 `"YYYY년 M월 D일 요일, H시 MM분"`(쉼표+24시간제+초 없음), 영어 로캘 `"Tuesday, May 20, 2025 1:04:56 AM"`, 요일 생략형 등) — 한컴 앱 버전·OS 로캘에 좌우되는 것으로 보이며 단일 정본이 없어 정직 보류(curve·connectLine과 같은 원칙, 무근거 포맷 추정 금지). 패키지 루트 속성(`version`/`unique-identifier`/`id`)은 실코퍼스 전량 빈 문자열이라 모델링 안 함. 왕복 바이트 보존 확인: 미변경 문서는 `Contents/content.hpf`가 저장/재오픈 전후 바이트 동일(직접 테스트). 원장 라우팅은 없음(opf:/dc: 네임스페이스는 HWPML이 아니라 coverage_ledger의 스키마-전집 축 밖 — settings.py/version_part.py와 같은 부류). 실한컴 렌더 검증: `docs/openrate/report-v16.json` authored-document-metadata 스트라텀(title/creator/subject/keyword 로테이션 + 빈 값 1건 + created_date/modified_date 명시 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-10 측정) — 이 영역의 첫 독립 실한컴 판정 |
| 바탕쪽 | Parse·Create(experimental)·Render-verified | `doc.parts.master_pages`(읽기, 6.4 트레인⑮)·`.add_master_page`(쓰기, 6.13 트레인㊻) + `doc.page.set_master_page`/`.master_page_refs`(절 참조 배선, `section_format.py`). 편집기 메뉴 표면 역매핑(트레인㊷)이 [부분 대응](읽기만, 쓰기 없음)으로 지목한 갭 — `HwpxOxmlMasterPage` 자신의 독스트링이 "읽기 전용... 쓰기 경로는 열지 않는다"고 명시했던 6.4 트레인⑮의 의도적 스코프 축소를 이번에 닫는다. 유일한 실 예시(`error__20250808__2015년_12월_재난안전종합상황_분석_및_전망.hwpx`)로 역설계: 파트 파일명·매니페스트 `opf:item id`·`masterPage` 루트 자신의 `id`·절의 `hp:masterPage/@idRef` 넷 다 **같은 문자열**("masterpage0")을 쓴다. 매니페스트에 이 파트는 있으나 spine `opf:itemref`는 없다(바탕쪽은 읽기 순서 파트가 아니다). `hp:secPr`의 자식 시퀀스에서 `hp:masterPage`는 맨 끝(pageBorderFill들 뒤)에 오고 `masterPageCnt`가 그 개수와 일치 — `SectionProperties.add_master_page_reference`가 그대로 append(멱등, 중복 idRef 무시)·카운트 동기화. `masterPage`의 `hp:subList`는 `textWidth`/`textHeight`를 실 예시가 페이지 여백에서 계산한 값으로 쓰지만 그 공식을 역산할 근거가 1건으로는 없어 다른 subList 빌더의 정직한 기본값("0")을 그대로 둔다(curve와 같은 원칙). 신규 바탕쪽이 여러 절 사이에서 공유/독립되는 관행 자체는 실증 근거가 없어 "파트 하나 생성 + 호출자가 지정한 절마다 명시적 배선"이라는 최소 계약만 제공한다. 실한컴 렌더 검증: `docs/openrate/report-v17.json` authored-master-page 스트라텀(page_type/page_number/page_duplicate/page_front 로테이션 + 두 번째 바탕쪽 생성으로 파트 인덱스 자동증가 확인, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-11 측정) — **패키지에 새 파트를 싣는 쓰기 경로가 실한컴을 통과한 첫 확인**(이전에 자체 발견·수리한 매니페스트 dirty 결함의 반증이기도 하다) |
| 날짜/시간·교정 부호·파일 이름 필드 | Create(experimental)·Render-verified | `HwpxOxmlParagraph.add_date_field`/`.add_proofreading_mark`/`.add_path_field`(`oxml/field_marks.py`, 6.13 트레인㊻+6.14 트레인㊽b). 팀장 실한컴 macOS GUI 프로브①③(2026-08-11) gold(`tests/fixtures/gui_probes/date_and_proofreading_mark.hwpx`) 역설계 — `add_hyperlink`의 3-run 격리와 달리 세 필드 다 단일 `hp:run` 안에 `ctrl(begin)`/`t`/`ctrl(end)`을 나란히 담는다(선택군 반복 허용은 DEV-017이 이미 확인). **날짜/시간**(`type="DATE"`): `id`/`fieldid` 독립 난수·`dirty="0"`(TOC와 달리 재계산 트리거 아님)·`hp:parameters`(`Prop=8`·`Command`=포맷 미리보기 문자열·`DateNation`="KOR"·`DateFormat`="YYYY년 M월 D일") — 관측 포맷 1건뿐이라 이 값만 지원(typed 거부, curve·connectLine과 같은 원칙), 캐시 텍스트는 호출자가 계산해 넘긴다. **교정 부호**(`type="PROOFREADING_MARKS_SIGN"`): 스키마 선언값(`PROOFREADING_MARKS`)과 실측이 달라 **DEV-043**으로 등재 — `hp:parameters`(`Prop=0`·`Command="$RevisionSign;N;"`, 캐시 텍스트 없음), 인덱스가 확인된 건 "띄움표"(N=1) 하나뿐이라 그것만 지원(대화상자 21종 중 나머지 20종은 인덱스-기호 대응표 근거 없음). **파일 이름**(`type="PATH"`, 6.14 트레인㊽b): GUI 프로브 불필요 — 트레인㊺가 이미 확보한 실 코퍼스 계약(`tests/fixtures/markdown_export/99_all_in_one_stress.hwpx`, 페이지 머리말 안) 그대로. `hp:parameters`(`Prop=8`=DATE와 공유값·`Command`=`Format`=`"$F"` 리터럴 고정), 캐시 텍스트는 관측 예시에서 파일명 그대로(호출자가 계산해 넘김, 전체 경로인지 파일명뿐인지조차 표본 1건으로는 확정 못 함). 이 세 필드를 정밀 대조하다 `hp:fieldEnd`가 `beginIDRef` 말고도 `fieldid`(fieldBegin 자신의 `fieldid`와 같은 값)를 반복해서 갖는다는 사실도 새로 확인 — 날짜/교정 부호의 기존 구현엔 이게 빠져 있었다(이미 v18 실한컴이 수용은 했으나 gold와 완전히 일치시키려 세 필드 전부 소급 수정). 실한컴 렌더 검증: `docs/openrate/report-v18.json` authored-date-field·authored-proofreading-marks 두 스트라텀(cached_text/배치/반복/char_pr_id_ref/표제 합성 로테이션 각 5건, 표 합성 포함, 10건), macOS Hancom GUI 오라클 10/10 render_checked·0 render_failed(2026-08-11 측정) — **팀장 자신의 GUI 리버스로 확보한 계약이 같은 편집기로 되돌려 검증된** 첫 사례(계약 출처와 검증 오라클이 동일 애플리케이션). 파일 이름 필드는 `docs/openrate/report-v19.json` authored-path-field 스트라텀(cached_text/배치/반복/char_pr_id_ref/표제 합성 로테이션, 5건 — fieldEnd/@fieldid 소급 수정을 실제로 담은 첫 배치), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-11 측정)로 마찬가지로 검증 완료 |
| 문서 옵션·호환성 | Parse·Preserve·Edit·Render-verified | `doc.parts.set_compatible_document_target_program`·`set_layout_compatibility_flags`·`set_doc_option_link_info`·`set_paragraph_auto_spacing`(6.6+). 읽기는 5.x부터(`hh:compatibleDocument`/`docOption`, cycle 6.1 `settings.py`/`header.py`) — 6.6이 쓰기 쪽. 실코퍼스 47/47 리버스로 계약 확정: `compatibleDocument@targetProgram`은 `"HWP201X"`만 관측·`layoutCompatibility`는 플래그 0개(스키마 48종 선언 대비)·`docOption/linkinfo`는 `path` 항상 빈 문자열·`footnoteInherit` 항상 `"0"`·`pageInherit`만 실제로 갈림(8/47 `"1"`). `hh:paraPr/hh:autoSpacing`은 `_apply_paragraph_margins`/`_apply_paragraph_line_spacing`이 이미 쓰는 자손-순회 관용구를 재사용하지만, 그 둘과 달리 실 autoSpacing은 `hp:switch`로 안 감싸인다(1832/1832 직속 자식, DEV-018과 대조 확인). 불리언은 전부 `"0"`/`"1"`(DEV-006과 같은 관례, `"true"`/`"false"` 0건). `HwpxOxmlHeader`의 owner 파일(`header_part.py`)이 1600줄 캡에 헤드룸이 없어(1599/1600) 새 모듈(`oxml/header_compat.py`)의 자유함수로 산다. 실한컴 렌더 검증: `docs/openrate/report-v10.json` authored-compat 스트라텀, macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-07 측정) — **관측 밖 조합(targetProgram=HWP2018·실제 layoutCompatibility 플래그·footnoteInherit=1·비어있지 않은 linkinfo path) 전부 포함해서도 15/15 수용**, 즉 실한컴의 실제 수용 경계가 실코퍼스 관측 분포보다 넓다는 게 확인됐다(모든 프로브 축에서 거부 0건) |
| 라이선스 표시(CCL) | Parse·Create | `doc.parts.set_license_mark(mark_type=, flag=, lang=)`/`.remove_license_mark()`(`oxml/header_compat.py`, P4). 팀장 실한컴 GUI 프로브 gold(`tests/fixtures/gui_probes/license_mark_ccl.hwpx`, "입력 > CCL 넣기…") 역설계 — `hh:docOption` 안 `hh:linkinfo` 바로 뒤 형제로 ``. **트레인㊺의 판정("CCL 전용 요소는 없다, 그림+하이퍼링크 조합일 뿐")을 실측이 뒤집었다** — 코퍼스 67파일 0건이라 실사용 근거가 없었을 뿐, 이 메뉴는 실제로 전용 문서 수준 레코드를 쓴다(배지 그림이 별개라는 그 판정의 나머지 절반은 맞다 — 배지는 라이선스 증서 URL을 `href`로 단 평범한 `hp:pic`이고, 이 setter는 문서 수준 레코드만 건드린다). **스키마 편차**: `Header XML schema.xml`의 `DocOptionType`은 `type`을 `xs:unsignedInt use="required"`로 선언하는데 실물은 문자열 `"CCL"`이다 — 스키마만 보고 `int`로 선언돼 있던 읽기 모델(`LicenseMark.type`)은 실한컴 CCL 문서를 `to_model()`로 읽는 순간 `ValueError: Invalid integer value: 'CCL'`로 터졌고, 이 트레인이 실측을 따라 `str`로 고쳤다(DEV-043과 같은 실측 우선 부류). `flag`/`lang`은 관측 `0`/`6`이라 정수 유지 — 관측이 1건뿐이라 값 범위는 강제하지 않는다(`set_compatible_document_target_program`과 같은 원칙, `mark_type` 빈 문자열만 typed 거부). 실한컴 렌더 검증: `docs/openrate/report-v22.json` authored-license-mark 스트라텀(관측값 CCL/0/6·flag/lang 변주·**미관측 type 문자열 "KOGL"(경계)**·재설정 멱등, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-18 측정) — 미관측 type 문자열도 수용(수용 경계가 관측 분포보다 넓다) |
| 페이지 레이아웃(용지·여백·머리말/꼬리말·쪽번호·단·줄번호·격자·요소 숨김) | Edit·Create·Render-verified | `doc.page`(6.8 트레인㉚ 등재, 코드 자체는 이전 사이클부터 존재). 최초 5개(`set_visibility`/`set_line_numbers`/`set_grid`/`restart_page_number`/`hide_page_elements` → `hp:visibility`/`hp:lineNumberShape`/`hp:grid`/`hp:newNum`/`hp:pageHiding`, `docs/openrate/report-v4.json` authored-page-structure·`report-v6.json` authored-pagecontrol, 각 15/15 render_checked)에 이어 **6.9 트레인㉜**이 나머지 실질 표면을 마저 검증: `set_size`/`set_margins`/`set_header`/`set_footer`/`set_page_number`/`set_columns` → `hp:pagePr`/`hp:margin`/`hp:header`/`hp:footer`/`hp:colPr`/`hp:pageNum`, `docs/openrate/report-v12.json` authored-pagelayout 15/15 render_checked·0 render_failed(2026-08-07 측정, header 제거 경로도 이 배치에서 함께 확인). 11개 요소 전부 `by-capability-area+openrate-corpus`. **남은 정직한 여백**: `hp:autoNum`은 각주/목록류 자동번호와 공유하는 요소라 의도적으로 미등록(실제 표시 요소인 `hp:pageNum`은 검증됨, DEV류 무근거 승격 회피 원칙과 동일), `setup()`은 위 이미 검증된 setter들을 그대로 호출하는 합성 편의 메서드라 별도 프로브가 없다, `remove_footer`는 `remove_header`와 대칭이나 v12가 이름으로 지목한 건 후자뿐이다. **6.12 트레인㊸ 갭③**이 `text_direction`/`set_text_direction`(`hp:secPr` 자신의 `textDirection`/`textVerticalWidthHead` 속성, 편집기 메뉴 표면 역매핑이 찾은 신규 갭)을 추가했다 — 실코퍼스 67파일 전수(모든 fixture 하위 디렉터리) 측정: `hp:secPr` 74건 전부 `textDirection="HORIZONTAL"`·`textVerticalWidthHead="0"`, `VERTICAL`/`VERTICALALL` 실사용 0건. 그래도 이 값은 새 구조를 추측하는 게 아니라 스키마가 명확히 선언한 열거값(`HORIZONTAL`/`VERTICAL`/`VERTICALALL`)을 이미 정상 파싱·직렬화되는 기존 요소(`hp:secPr`)에 그대로 쓰는 것뿐이라 구조적 추측 위험이 없다. 실한컴 렌더 검증: `docs/openrate/report-v16.json` authored-text-direction 스트라텀(`VERTICAL`×2·`VERTICALALL`×2·명시적 `HORIZONTAL`×1, `vertical_header_footer` True/False 조합), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-10 측정) — **이 실코퍼스(67파일 전수)에 실사용 0건이던 `VERTICAL`/`VERTICALALL`이 이 프로젝트에서 실한컴을 통과한 첫 문서**다. **원장 라우팅은 없음**: `textDirection`은 요소가 아니라 속성이고, 그 부모 `hp:secPr` 자체는 모든 문서에 항상 존재하는 컨테이너(위 5+6개 자식 요소를 이미 개별 등록)라 `hp:secPr`를 통째로 등록하면 이 값을 한 번도 안 건드린 문서까지 전부 위양성 처리된다 |
| 문자 서식(굵게·기울임·밑줄·글꼴·크기·색 등) | Edit·Create·Render-verified | `doc.styles.ensure_run`(6.8 트레인㉚ 등재, 코드는 이전 사이클부터 존재 — `hh:charPr`는 이미 "문단·표 저작/편집" 영역이 요소째 소유하고 있어 이 영역은 캐파빌리티 키워드 배선 없이 매트릭스 산문으로만 근거를 남긴다). **17/17 키워드 인자 전부 실한컴 증거 확보 완결(6.12 트레인㊶)**: `bold`/`color`/`size`(`docs/openrate/report-v4.json` authored-named-style, 10/10 render_checked), `outline`/`emboss`/`engrave`/`script`(sup/sub, `docs/openrate/report-v7.json` authored-charformat, 15/15 render_checked), `underline`/`underline_shape`/`underline_color`/`strike`/`strike_shape`/`ratio`/`letter_spacing`/`shadow`(`docs/openrate/report-v14.json` authored-charformat2, 5/5 render_checked, 2026-08-08), 그리고 마지막 잔존이던 `italic`(v14의 authored-charformat2 뱅크가 빠뜨렸던 항목 — `docs/openrate/report-v15.json` authored-charformat-italic 스트라텀, italic 단독+bold/underline/color 조합, macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed, 2026-08-08 측정)까지 전부 확보됐다. `base_char_pr_id`는 XML 요소가 아니라 파이썬 레벨 조합 방식(기존 charPr 복제 후 오버라이드)이라 이 17개 카운트에서 애초에 제외(등록 대상 없음), `highlight`는 별도 매트릭스 행("형광펜(하이라이트)")이 이미 독립적으로 Render-verified라 이 행의 카운트에 안 넣는다 — 두 제외 사유 모두 이 행 초판부터 일관됨 |
| 목록 서식(글머리표·번호매기기) | Create·Edit·Render-verified | `doc.styles.apply_list_format`/`.ensure_numbering`(6.8 트레인㉚ 등재, 코드는 이전 사이클부터 존재). 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-listformat 스트라텀(bullet/number kind × level 1·2 로테이션, bullet_char/number_format/start 오버라이드), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정) — 이 영역의 첫 독립 실한컴 판정. `hh:bullet`/`bullets`/`numbering`/`numberings`/`paraHead`가 원장에 새로 등록됐다(6.11 트레인㊳, 이전엔 대응 capabilityArea 자체가 없었다) — `hh:heading`(paraPr 안에서 numbering/bullet을 가리키는 polymorphic idRef)은 document_merge 등 다른 경로도 공유하는 저수준 요소라 의도적으로 미등록. **6.13 트레인㊻**: `ensure_numbering`/`apply_list_format`이 세 번째 `kind="outline"`을 받는다 — `hh:heading type="OUTLINE"`이 이 목록 서식과 **같은** `hh:numbering`/`hh:paraHead` id-space를 쓴다는 사실(`bind_outline_level`, `_document/headings.py`)을 그대로 재사용해, `add_heading`/`apply_paragraph_format(outline_level=)`이 항상 고정 `idRef="0"`(스켈레톤 개요 스타일)만 참조하던 것과 달리 **커스텀 개요 번호 모양**(numFormat/start)을 만들 길을 연다. 실코퍼스 67파일 전수: `hh:heading type="OUTLINE"` 451건 전부 `idRef="0"`뿐이라 커스텀 idRef 조합은 실코퍼스 밖이지만 — 재사용하는 `hh:numbering`/`hh:paraHead` 구조 자체는 v14에서 이미 render-verified라 구조적 추측 위험은 없었고, 이번에 실측으로도 확인됐다: `docs/openrate/report-v17.json` authored-outline-numbering 스트라텀(level/numFormat/start 로테이션 + 기존 `apply_paragraph_format(outline_level=)` 수준 증가와 합성한 1건, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-11 측정) — 합성 레코드가 "`apply_paragraph_format(outline_level=)`이 paraPr을 새로 만들 때마다 `idRef="0"`을 하드코딩해 커스텀 번호 모양을 무음으로 덮어쓴다"는 기존 동작(버그 아님, 충실 재현)도 같이 실증했다. `apply_paragraph_format(outline_level=)`을 통한 개요 적용/해제·수준 증감은 코드가 이미 완비돼 있었다(신규 코드 없음, 이번 트레인이 확인만 함) |
| 글꼴 등록 | Create·Render-verified | `doc.styles.ensure_font`(6.8 트레인㉚ 등재, 코드는 2026-08-04 감사 §2 갭#1 이후 사이클에서 구현됐으나 캐파빌리티 미등재 상태였다). 실한컴 렌더 검증: `docs/openrate/report-v5.json` authored-fontface 스트라텀(lang 7블록 전부·substFont on/off 조합), macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed — 실제로 참조된 fontRef까지 확인(선언만 하고 고아로 남기지 않음) |
| 표 탐색 기반 채움(라벨 매칭 네비게이션) | Edit·Render-verified | `doc.tables.fill_by_path`/`.find_cell_by_label`(6.8 트레인㉚ 등재, 코드는 이전 사이클부터 존재). "양식 채움(byte-splice)" 영역과는 다른 메커니즘(byte-splice가 아니라 라벨 매칭 기반 구조적 셀 채움) — 혼동 주의. 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-tablenavfill 스트라텀(단일-홉 right/down 4건 + multi-hop `합계 > down > right` 1건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정). **원장 라우팅은 없음** — 기존 `hp:tc`/`hp:t` 텍스트를 치환만 할 뿐 새 요소를 안 만든다(document-merge·mail-merge·find-replace와 같은 이유로 CAPABILITY_KEYWORDS 등록 요소 0개) — 등급 갱신은 이 산문과 report-v14.json 직접 인용으로만 |
| 찾아바꾸기 | Edit·Render-verified | `doc.text.replace`/`.find_runs`(6.8 트레인㉚ 등재, 코드는 이전 사이클부터 존재). 실한컴 렌더 검증: `docs/openrate/report-v14.json` authored-findreplace 스트라텀(무제한 치환 + `char_pr_id_ref`로 좁힌 스타일 한정 치환 교대), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정). **원장 라우팅은 없음** — 기존 `hp:t` 텍스트만 치환할 뿐 새 요소를 안 만든다(document-merge·mail-merge·table-navigation-fill과 같은 이유) — 등급 갱신은 이 산문과 report-v14.json 직접 인용으로만 |
| 하이퍼링크·책갈피 | Create·Render-verified | `doc.refs.add_hyperlink`/`.add_bookmark`(6.8 트레인㉚에서 "네이티브 목차(TOC)/상호참조" 영역에서 분리 — 그 영역의 Render-verified 근거는 TOC 구조·페이지 정합뿐이라 하이퍼링크·책갈피를 한 번도 독립적으로 커버한 적이 없었다, 편집기 표면 인벤토리 트레인㉙ 발견). **6.9 트레인㉜**: `docs/openrate/report-v12.json` authored-hyperlink-bookmark 스트라텀(북마크 생성 후 외부 URL/문서 내 `#앵커` 하이퍼링크 교대), macOS Hancom GUI 오라클 15/15 render_checked·0 render_failed(2026-08-07 측정) — 이 영역의 첫 독립 실한컴 판정. `hp:bookmark`는 요소 직접 등록(`by-openrate-corpus`), `hp:fieldBegin`(하이퍼링크 자신의 컨트롤)은 CLICKHERE/TOC/CROSSREF와 공유하는 저수준 요소라 여전히 요소 등록 없음 — 영역 등급만 실측으로 갱신, 원장 오염 경로는 그대로 차단 |
| 메일머지(placeholder 템플릿 배치 생성) | Edit·Render-verified | `hwpx.tools.mail_merge.merge_template_rows`(6.8 트레인㉛ 등재, 코드는 이전 사이클부터 존재 — 편집기 표면 인벤토리 트레인㉙·계층 판정 트레인㉚이 찾을 때까지 캐파빌리티·매트릭스 어디에도 등재가 없었다). `{{field}}`/`${field}`/`<>` 세 플레이스홀더 문법을 지원하는 템플릿 1개 + 행 데이터(csv/json/시퀀스)로 문서 N개를 배치 생성 — 기존 `hp:t` 텍스트를 치환하는 것이라 새 요소를 만들지 않는다(Create가 아니라 Edit). `strict`/`fit_policy`/`value_sanitizer` 등 운영 옵션 다수. 실한컴 렌더 검증: `docs/openrate/report-v13.json` authored-mailmerge 스트라텀(세 플레이스홀더 문법 각각 겨냥하는 전용 템플릿으로 회전), macOS Hancom GUI 오라클 10/10 render_checked·0 render_failed(2026-08-08 측정) — 이 영역의 첫 독립 실한컴 판정 |
| 메일머지 표시 필드 | Create(experimental) | `HwpxOxmlParagraph.add_mail_merge_field(name, cached_text=)`(`oxml/field_marks.py`, P4). 팀장 실한컴 GUI 프로브 gold(`tests/fixtures/gui_probes/mailmerge_display_fields.hwpx`, "도구 > 메일 머지 > 메일 머지 표시 달기"로 필드 2개 삽입) 역설계 — 위 「메일머지(placeholder 템플릿 배치 생성)」가 **기존 `hp:t` 텍스트를 치환**하는 Edit인 것과 달리 이쪽은 `hp:fieldBegin type="MAILMERGE"` 자체를 새로 만드는 Create다(그래서 별도 영역). DATE/PATH와 같은 단일 run 안 `ctrl(begin)`/`t`/`ctrl(end)` 배치에 더해 **끝에 빈 `hp:t`가 하나 더** 붙고(gold 2/2), `hp:fieldBegin`에 `metaTag` 속성이 **없다**(DATE/교정 부호/PATH gold는 셋 다 `metaTag=""`) — 필드 종류마다 실측이 갈리는 유일한 속성. `hp:parameters`(`cnt="5"`): `Fiexde=1`(**오타 철자가 실물·한컴 공식 문서 공통의 정본이라 정정 금지** — `trackchageConfig`·memo의 `NOMAL`과 같은 부류) · `Prop=8`(DATE/PATH와 같은 캐시 필드류 공유값) · `Command`/`FieldValue`가 둘 다 필드 이름 그대로 · `FieldType="USER_DEFINE"`. 캐시 텍스트 기본값 `{{이름}}`은 `hwpx.tools.mail_merge`가 인식하는 플레이스홀더 문법이라 이 필드로 저작한 템플릿이 그 배치 생성기에 그대로 물린다. gold의 두 필드는 `id`가 다른데 `fieldid`는 같은 값을 공유하나, 표본이 문서 하나뿐이라 채번 규칙을 역산할 근거가 없어 DATE/PATH와 같은 독립 난수를 쓴다. 실한컴 렌더 검증: `docs/openrate/report-v22.json` authored-mailmerge-field 스트라텀(단일/2필드/커스텀 캐시/**한글 필드명(경계)**/표 셀 안 필드, 5건), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-18 측정) |
| 문서 끼워 넣기(문서 병합) | Create·Render-verified | `hwpx.tools.document_merge.append_document`/`.insert_document`(6.9 트레인㉝ v1, 6.10 트레인㊱ v2 — 편집기 표면 인벤토리 트레인㉙의 macOS 메뉴 전수 스캔이 찾은 신규 갭, "입력→문서 끼워 넣기…"). 다른 HWPX 문서의 본문을 연 문서에 끼워 넣는다 — 핵심은 헤더가 소유한 8개 공유 자원(charPr/paraPr/style/borderFill/tabPr/numbering·bullet/memoPr/fontfaces)을 원본에서 새 id로 복제해 재매핑하는 것이지 단순 복사가 아니다(그대로 복사하면 대상 문서의 기존 id를 조용히 가로챈다). 한컴 편집기 실측(팀장, 2026-08-07): "입력→문서 끼워 넣기…" 패널의 4개 고유 옵션(글자 모양 유지·스타일 유지·쪽 모양 유지·문단 모양 유지, 기본값 전부 해제=흡수)이 정책 축의 실측 근거. v2는 이 4축을 한컴과 같은 이름의 매개변수(`keep_character_shape`/`keep_style`/`keep_paragraph_shape`/`keep_page_shape`)로 노출한다 — 기본값은 v1의 기출하·실검증 동작과 동일(글자·스타일·문단 모양=유지, 쪽 모양=흡수), 반대 방향은 매핑 실측이 없어 typed 오류로 정직 거부(과설계 안 함). v2는 MEMO 병합도 지원한다(DEV-042): `hp:memogroup`은 절 레벨 형제라 원본의 모든 절에서 참조된 `hp:memo`를 찾아 복제, 새 메모 id 채번 후 body 문단 복제본과 같은 리스트로 묶어 기존 축1 재매핑(charPr/paraPr/style/memoShapeIDRef)에 그대로 태운다. 실측으로 찾은 결함 8건을 수정(v1 6건 + v2 2건): 스타일 자신의 기본 charPr/paraPr 누락, `HwpxOxmlDocument.char_properties` 캐시 미무효화, `linkListIDRef`/`linkListNextIDRef` 과잉거부(팀장 독립 검증 — 벤더드 코퍼스 5,891/5,891건이 전부 "0" 관용값인데 존재만으로 거부해 표가 든 모든 문서, 우리 자신의 `add_table` 산출물 포함, 병합이 불가능했다), 저장 경로 dirty-tracking 미설정(가장 심각 — `target_header.mark_dirty()`를 호출하지 않아 헤더에 새로 추가한 요소가 저장 시 조용히 누락되는데, 그림이 있는 병합만 우연히 `add_image`의 부수효과로 가려져 있었다), `hp:secPr` 제거가 통째 run을 지워 표·텍스트까지 함께 삭제(실코퍼스 표 문서에서 secPr·표·텍스트가 한 run에 공존, 수정 후 secPr만 개별 제거), 새 id 채번 순서가 프로세스마다 비결정적(`set` 순회를 `sorted()`로 교체), `check_id_integrity` 자신의 `memoShapeIDRef="0"` 오탐(fresh 문서의 secPr 스켈레톤 기본값을 memo_shapes 테이블이 채워지는 순간부터 댕글링으로 오판 — 병합과 무관한 사전 존재 결함, `id_integrity.py`의 `_ALLOWED_SENTINELS`로 수정), `hp:fieldEnd`의 `fieldid` 미재생성(원본 문서의 raw uuid4().hex 값이 복사된 내용에 그대로 남음 — v14 생성기 자체 결정성 검사로 발견, `begin_fieldid_map`으로 수정). `hp:connectLine`(스마트 연결선)·`chartIDRef`·실체인 `linkListIDRef`가 있으면 fail-closed 거부. 참조 무결성은 재발명하지 않고 기존 `hwpx.tools.id_integrity.check_id_integrity`로 검증(설계 문서: `docs/2026-08-08-document-merge-contract.md`). 실한컴 렌더 검증: `docs/openrate/report-v13.json` authored-docmerge 스트라텀(v1, 10건 중 4건이 표 포함 — 위 결함 셋이 전부 표 병합에서 터졌던 형태를 정면으로 겨냥), macOS Hancom GUI 오라클 10/10 render_checked·0 render_failed(2026-08-08 측정). v2(4축 매개변수+MEMO 병합)는 `docs/openrate/report-v14.json` authored-docmerge2 스트라텀(단일 메모/센티널 기본값/대상 기존 memogroup 합류/표 동반 4갈래, 4축 기본값 명시통과 호출), macOS Hancom GUI 오라클 5/5 render_checked·0 render_failed(2026-08-08 측정)로 검증 완료. 6.15: 클론 경로 감사(속성-클론 중첩 참조 앨리어싱 수리, `b81713e`)의 후속으로 v20(`cross-real-merge`, 우리 자신이 생성한 문서가 아니라 실한컴이 저장한 문서끼리 병합 — v13의 성긴 헤더로는 이 앨리어싱 결함이 애초에 안 보였을 것이라는 지적)을 실행: 실 문서 3종 쌍방향 병합·중간 삽입 3건·필드 보유 소스 2종·3단 연쇄 병합 12건, macOS Hancom GUI 오라클 12/12 render_checked·0 render_failed(2026-08-12 측정) — 앨리어싱 수리 후 재현이 사라졌음을 실 소스로 재확인. 세 버전 모두 같은 스트라텀 그룹(document_merge)이라 원장 라우팅은 여전히 없음(위 산문 근거만) |
| 덧말·글자 겹치기 | Parse·Create·Render-verified | `doc.shapes.add_dutmal`/`.add_composed_character`(6.9 트레인㉞). `hp:compose`(글자 겹치기/원문자·합자)는 읽기 모델이 이전 사이클(commit `6f88e2e`)부터 있었으나 저작 API·캐파빌리티 등재가 없었다 — 이번에 `add_composed_character`로 채우고 같은 `body.ComposedCharacter`/`_composed_character_to_xml` 경로를 저작에도 재사용(파싱·저작 한 코드 경로). `hp:dutmal`(덧말)은 이번에 처음 리버스엔지니어링 — 실코퍼스 표본 정확히 1건(`reader_writer__SimpleDutmal.hwpx`, hwpxlib 왕복 픽스처), macOS 편집기 메뉴 전수 스캔(트레인㉙)이 1급 메뉴 항목임은 확인했으나 그 이상의 빈도 근거는 없다 — 정직 고지. 그 1건이 스키마의 두 주장을 직접 반증한다(`option`은 스키마상 `fixed="4"`인데 실측 `"0"`, `szRatio`는 `xs:positiveInteger`인데 실측 `"0"` — DEV-041). 저작 API는 이 실측값을 기본값으로 채택하고 스키마 주장을 검증 규칙으로 강제하지 않는다. 둘 다 `hp:t`의 자식이 아니라 `hp:run` 직속(스키마 문서 순서와 실제 위치가 다름, 실코퍼스로 확인). 실한컴 렌더 검증: `docs/openrate/report-v13.json` authored-dutmal-compose 스트라텀(dutmal 기본값을 실측값 그대로 두고 검증 — 스키마 주장이 아니라 우리 채택을 오라클이 판정), macOS Hancom GUI 오라클 10/10 render_checked·0 render_failed(2026-08-08 측정) — `hp:compose`/`hp:dutmal`/`hp:mainText`/`hp:subText` 4개 요소 전부 원장에 `by-openrate-corpus` 반영(코드-쓰기 분류기가 `_qualified_tag(tag, "literal")`류 관용구를 못 잡던 갭도 이번에 수동 override로 같이 메움) |
## 6.0 표면 위치
능력 영역이 `HwpxDocument` 의 어느 자리에 사는지. 이 표는 능력 레지스트리
(`hwpx.capabilities._CAPABILITY_AREAS`)의 `namespace` 필드에서 생성되며,
`python -m hwpx.capabilities --verify` 가 그 자리가 실재하는지 검사한다.
| 능력 영역 | 6.0 위치 |
|---|---|
| 문단·표 저작/편집 | 루트 — `doc.add_paragraph` · `doc.add_heading` · `doc.add_section` |
| 표 구조 변경(행·열·표 삭제/삽입, 열 오토핏) | `doc.tables` |
| 표 생성(병합·중첩 포함) | 루트 — `doc.add_table` |
| 양식 채움(byte-splice) | `doc.tables` |
| 편집 계획 실행(edit plan) | 모듈 — `hwpx.plan` |
| 도형 저작(선·사각형·타원) | `doc.shapes` |
| 저수준 도형·컨트롤 탈출구 | `doc.shapes` |
| arc·polygon·curve·connectLine | `doc.shapes` |
| 그룹 개체(컨테이너) | `doc.shapes` |
| 그림 삽입/치환 | 루트 `doc.add_picture` + `doc.media` (이진 항목) |
| 차트 | `doc.shapes` |
| 수식 | `doc.shapes` |
| 문단 첫 글자 장식(드롭캡) | `doc.shapes` |
| 변경추적(redline) | `doc.tracking` |
| 메모(코멘트) | `doc.notes` |
| 각주/미주 | `doc.notes` |
| 네이티브 목차(TOC)/상호참조 | `doc.refs` |
| 차례 숨기기·제목 차례 표시 | `doc.refs` |
| 색인 표시 | `doc.refs` |
| 암호화 HWPX | 미지원 |
| HWP 5.x 바이너리 | 미지원 |
| 누름틀(form field) 생성 | `doc.fields` |
| 체크박스 양식개체 | `doc.fields` |
| 형광펜(하이라이트) | `doc.text` |
| 테두리 채우기(이미지·그라데이션) | `doc.styles` |
| 문서 정보(메타데이터) | `doc.parts` |
| 바탕쪽 | `doc.parts`/`doc.page` |
| 날짜/시간·교정 부호·파일 이름 필드 | `doc.fields` |
| 문서 옵션·호환성 | `doc.parts` |
| 라이선스 표시(CCL) | `doc.parts` |
| 페이지 레이아웃(용지·여백·머리말/꼬리말·쪽번호·단·줄번호·격자·요소 숨김) | `doc.page` |
| 문자 서식(굵게·기울임·밑줄·글꼴·크기·색 등) | `doc.styles` |
| 목록 서식(글머리표·번호매기기) | `doc.styles` |
| 글꼴 등록 | `doc.styles` |
| 표 탐색 기반 채움(라벨 매칭 네비게이션) | `doc.tables` |
| 찾아바꾸기 | `doc.text` |
| 하이퍼링크·책갈피 | `doc.refs` |
| 메일머지(placeholder 템플릿 배치 생성) | 모듈 — `hwpx.tools.mail_merge` |
| 메일머지 표시 필드 | `doc.fields` |
| 문서 끼워 넣기(문서 병합) | 모듈 — `hwpx.tools.document_merge` |
| 덧말·글자 겹치기 | `doc.shapes` |
5.x 의 옛 이름은 6.x 동안 계속 답하되 `DeprecationWarning` 을 내고 7.0 에서
사라진다 — 대응표는 `docs/migration-6.0.md`.
## 상태 판정 근거 요약
- **Render-verified**는 실제 한컴 렌더 오라클(Windows COM `SaveAs("PDF")` 또는 Mac GUI
refresh→render)이 붙어 pass가 나온 능력에만 붙인다. 렌더를 돌리지 않았거나 한컴이
export를 거부한 경우는 `not_performed`/`render_unavailable`로 정직 집계하고 이 등급을
주지 않는다.
- **Preserve**는 [Safe Write Contract](safe-write-contract.md)의 `untouchedPartPayloads`
측정에 근거한다. 손대지 않은 part는 patch 경로에서 압축 해제 페이로드가 바이트
동일하게 유지되며, 코퍼스 v2에서 497/497로 실측됐다.
- **Unsupported-and-rejected**는 입력을 무음 처리하지 않고 예외로 거부함을 확인한
경로에만 붙인다(암호화 content, HWP 5.x 바이너리). 두 경로 모두 실제 예외를 관찰해
판정했다.
- **Unsupported-but-preserved**는 생성/편집 API가 없지만 기존 요소가 patch 저장에서
보존됨을 뜻한다(차트). 새로 만들거나 편집하는 기능은 제공하지 않는다.
## 관련 문서
- [실측 코퍼스 메트릭](corpus-metrics.md) — 각 수치의 분모·판정자·측정 방법론
- [안전한 쓰기 계약](safe-write-contract.md) — 보존 등급과 `MutationReport` 영수증