문단·표 저작/편집 |
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 형제로 텍스트 앞에 선다(<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-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=□, <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: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 바로 뒤 형제로 <hh:licensemark type="CCL" flag="0" lang="6"/>. 트레인㊺의 판정(“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}/<<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로 같이 메움)
|