지원 매트릭스 (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과는 다른 축이다. 상세는 실측 코퍼스 메트릭 참조.

매트릭스

능력 영역

상태

증거

문단·표 저작/편집

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:pcolumnBreak 속성(문단 인스턴스별 강제 단 나눔)을 저작한다 — 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/TableTypepageBreak/repeatHeader/rowCnt/colCnt뿐, ParaList XML schema.xml:2008), 순수 구조 편집이라 _insert_block_by_clone과 같은 fail-closed 원칙(병합 셀이 경계를 걸치면 거부)을 그대로 따른다. split_tablesplit_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비트로 직렬화된 것을 실측 확인)·curSzorgSz와 다르며·scaMatrix가 평행이동까지 얹은 비항등 행렬이라 그 관계식 근거가 없다. 다른 예시(error__20230818__test.hwpx, 미부착 subjectIDRef=0)는 파일명이 스스로 결함 사례임을 밝히고 scaMatrix가 e1=0인 퇴화 행렬이라 정본으로 못 쓴다. startPt/endPthp:connectLine에서는 hp: 네임스페이스(hp:linehc: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"), renderingInfotransMatrix 이동성분이 자기 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:imgadd_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", curSzorgSz와 다르게 "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:titleMarkhp: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 형제로 텍스트 에 선다(<hp:run><hp:ctrl><hp:indexmark><hp:firstKey>apple</hp:firstKey></hp:indexmark></hp:ctrl><hp:t>apple item paragraph.</hp:t></hp:run>). 타겟팅 계약은 같다 — 호출자가 대상 문단을 직접 지정하는 형태가 실 편집기의 “캐럿이 있는 문단”과 대응한다. 스키마 편차: ParaList XML schema.xml:209-216firstKey/secondKey를 둘 다 필수 시퀀스로 선언하는데(속성 0개, 두 키 모두 무타입) 실물은 1단계 색인에서 secondKey를 아예 생략한다 — add_new_numautoNumFormat 생략과 같은 부류로 실측을 따랐다(빈 문자열 키는 생략과 방출 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=□, <hp:formCharPr>필수 자식(없으면 한컴이 문서를 거부하는데 우리 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:fillBrushwinBrush/imgBrush/gradation 중 하나만 허용하는 선택형(Core XML schema.xml:650) — 상호 배타 typed 거부. fill_imagedoc.media.add_image가 등록한 이진 항목을 mode(관측 전량 TOTAL)로 참조하고, fill_gradient는 색 목록(≥2)·type(관측 전량 LINEARangle 등을 받는다. 실한컴 렌더 검증: 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.hpfopf:metadata(제목/작성자/주제/키워드/작성일/수정일). 편집기 메뉴 표면 역매핑(트레인㊷)이 찾은 신규 갭(“파일→문서 정보…”) — opf:metadata 자체는 모든 실 문서에 실재하나(실코퍼스 67파일 확인) 이전엔 opc/package.pyopf: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 무시)·카운트 동기화. masterPagehp:subListtextWidth/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:fieldEndbeginIDRef 말고도 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/linkinfopath 항상 빈 문자열·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:docOptionhh:linkinfo 바로 뒤 형제로 <hh:licensemark type="CCL" flag="0" lang="6"/>. 트레인㊺의 판정(“CCL 전용 요소는 없다, 그림+하이퍼링크 조합일 뿐”)을 실측이 뒤집었다 — 코퍼스 67파일 0건이라 실사용 근거가 없었을 뿐, 이 메뉴는 실제로 전용 문서 수준 레코드를 쓴다(배지 그림이 별개라는 그 판정의 나머지 절반은 맞다 — 배지는 라이선스 증서 URL을 href로 단 평범한 hp:pic이고, 이 setter는 문서 수준 레코드만 건드린다). 스키마 편차: Header XML schema.xmlDocOptionTypetypexs: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_elementshp: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_columnshp: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_footerremove_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}/<<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:fieldBeginmetaTag 속성이 없다(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:fieldEndfieldid 미재생성(원본 문서의 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", szRatioxs: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로 정직 집계하고 이 등급을 주지 않는다.

  • PreserveSafe Write ContractuntouchedPartPayloads 측정에 근거한다. 손대지 않은 part는 patch 경로에서 압축 해제 페이로드가 바이트 동일하게 유지되며, 코퍼스 v2에서 497/497로 실측됐다.

  • Unsupported-and-rejected는 입력을 무음 처리하지 않고 예외로 거부함을 확인한 경로에만 붙인다(암호화 content, HWP 5.x 바이너리). 두 경로 모두 실제 예외를 관찰해 판정했다.

  • Unsupported-but-preserved는 생성/편집 API가 없지만 기존 요소가 patch 저장에서 보존됨을 뜻한다(차트). 새로 만들거나 편집하는 기능은 제공하지 않는다.

관련 문서