3-10. HWPX를 열고, 읽었다는 것을 검증하다

지난 편의 마지막에는 HWPX가 예정이라고 적었다. 닷새 뒤에는 ZIP을 열고 XML 파트를 연결하며 본문·표·그림·수식의 원값을 검사하는 코드가 생겼다. 다만 이 변화는 Zig 코어의 읽기·검사 계층이다. 새 HTML 뷰어가 나온 것은 아니고, 제품 JS/WASM의 본문 API에도 아직 연결되지 않았다.

이 편은 2026년 9월 22일부터 26일 16:39 KST까지 공개 main에 들어간 142커밋을 다룬다. 기준 커밋은 6383b4dd다.

날짜커밋들어간 것
9/2222EMF+ 그림·선·사각형·타원·호의 장치 좌표 변환, 곡선 평가·분할
9/2326EMF+ 경로의 선분 근사와 경계 조립, HWPX ZIP 읽기·manifest 관계·header 리소스·차트 검사
9/2435section XML 트리·텍스트 이벤트, 패키지 바이트 무결성, 조건부 분기 선택, 표 격자·셀 검사
9/2539바탕쪽·표·줄 조각·구역 설정, 그림과 채움 이미지의 OPF 연결, WMF·TIFF·PCX 및 HWP5 이미지 진단
9/2620SVG·OLE 검사, JPEG·PNG 픽셀 검사, 본문·바탕쪽 텍스트 스냅샷과 독립 대조, 수식 script 검사

날짜와 개수는 위 커밋까지의 main 커미터 시각을 한국 시간으로 집계했다.

EMF: 레코드에서 좌표로

9월 21일까지가 EMF+ 레코드의 값과 변환 상태를 읽는 작업이었다면, 다음 이틀은 그 값을 실제 기하로 잇는 작업이었다. 이미지의 원본 사각형을 평행사변형으로 만들고, world·page 변환을 거쳐 장치 좌표에 놓는다. 같은 경로가 polyline·Bezier·Path·타원·호로 이어진다.

곡선은 평가·분할·선분 근사 단계를 나눴다. 닫힌 경로의 경계를 조립하고 marker와 dash 지점까지 따로 꺼낸다. 여기까지 갖춰야 뒤에서 채우기나 선 그리기를 붙일 수 있다. 기하를 계산하는 것과 화면에 그리는 것은 서로 다른 단계로 남아 있다. 호·파이 경계 근사 커밋이 이 구간의 끝이다.

HWPX: 압축을 푼 뒤에도 확인할 것이 많다

9월 23일 범위를 제한한 ZIP 엔트리 읽기가 들어갔다. 그 뒤에는 파일을 찾는 것보다 파일 사이 관계를 확인하는 작업이 이어졌다. manifest와 spine이 가리키는 파트, header의 리소스 ID, 문단과 run의 서식 참조, 언어별 글꼴, 그림의 BinData, 차트 XML을 차례로 연결했다.

9월 24일에는 파트마다 소유권을 가진 XML 트리를 만들고 본문을 순서 있는 이벤트로 읽었다. ZIP 바이트 무결성과 선언된 XML 문법 검사도 묶었다. 조건부 switch는 원문 양쪽을 관측하는 경로와 호출자가 지원한다고 명시한 분기를 고르는 경로를 구분한다. 그래야 검사 결과에서 원문이 사라지지 않고, 선택한 결과와도 대조할 수 있다.

표도 격자·셀 크기·여백·테두리 참조부터 읽었다. 이어 바탕쪽의 문단·표·차트·이미지 참조와 구역 쪽 설정, 각주·미주 모양, 줄 조각으로 범위를 넓혔다. 이 결과를 모으는 inspectKnown()은 성공하더라도 미해결 참조 진단을 담을 수 있다. 이름 그대로 현재 아는 항목을 검사하는 API이고, 문서 전체의 유효성이나 조판 완료를 판정하지 않는다. 검사 묶음의 계약에 이 경계가 적혀 있다.

그림을 찾는 것과 픽셀을 푸는 것

그림과 채움 브러시가 가리키는 OPF 항목을 찾은 뒤, 실제 바이트가 어떤 형식인지 검사했다. manifest의 이미지 후보 전체를 보는 경로도 따로 생겼다. 선언은 PNG인데 실제 바이트는 JPEG인 HWP5 BinData, PNG 끝 뒤의 0 패딩처럼 실파일에서 본 차이는 진단으로 남긴다.

9월 26일에는 JPEG 픽셀 검사를 명시적으로 켤 수 있게 하고, Exif 방향은 픽셀 복호화와 별도로 보고했다. PNG도 공통 RGBA 코어를 HWP 컨테이너 검사에 연결했다. PNG 연결 검증은 합성 경로와 실제 8×8 이미지의 Pillow 대조를 근거로 한다. 그 한 장의 일치가 전체 이미지 호환성을 뜻하지는 않는다. PNG 검사 범위와 근거를 함께 남겼다.

HWP5 문서 요약도 원값과 진단을 나눴다

HWPX 작업 사이에 HWP5의 문서 요약 스트림도 보완했다. 9월 25일에는 typed value의 예약 패딩을 검사하고, 코드페이지 없이 나타나는 특정 dictionary 마커를 별도 진단으로 컨테이너 보고서에 올렸다. 코드페이지를 추측하거나 그 마커를 정상 사전 이름으로 해석하지 않고 원문을 보존한다. 요약 스트림 검사 계약

읽은 텍스트를 어떻게 믿을 것인가

본문 텍스트 이벤트를 콜백에서만 소비하지 않고, 원본 문서가 해제된 뒤에도 보관할 수 있는 스냅샷으로 만들었다. 이어 Python의 zipfile·ElementTree로 같은 파일을 독립적으로 읽어 내용과 이벤트 순서를 비교했다.

저장소에 기록된 9월 26일 조사에서는 로컬 HWPX 후보 484개 가운데 ZIP 거부 6개와 암호화 2개를 따로 분류하고, 나머지 476개를 파일별로 대조했다. 본문 내용뿐 아니라 문단·run·text 경계, 인라인 이벤트 순서, 개수와 바이트 수도 비교했다. A<tab/>B와 AB<tab/>는 연결 텍스트가 같아도 순서 해시가 달라야 한다. 선택 분기 두 경우와 바탕쪽에도 대조를 이어 붙였다. 본문 스냅샷 검증 기록

이 숫자는 해당 로컬 표본의 XML 텍스트 이벤트 대조 결과다. 표본의 일부는 Git에 없는 reference/rhwp에 있어 깨끗한 체크아웃의 기본 테스트만으로 재현되지 않는다. 화면의 읽기 순서·글꼴·페이지 배치를 검증한 수치로도 쓰지 않는다.

마지막으로 수식 원문과 script 검사가 들어갔다. 같은 조사에서 수식이 있는 28개 문서의 직접 수식 23,236개와 script 내용이 독립 대조와 일치했다. 이는 문자열과 필드를 보존했다는 근거다. 수식 문법 해석과 수식 렌더러는 아직 남아 있다.

다음 단계까지 남은 거리

닷새 동안 HWPX는 예정에서 코어 검사 코드로 이동했다. 본문과 바탕쪽 텍스트를 소유하고, 리소스 관계와 여러 이미지 형식을 검사하는 기반이 생겼다.

다음 경계는 이 값들을 편집 가능한 문서 모델로 조립하고, 레이아웃과 렌더링에 연결하는 일이다. HWPX 편집·저장·무손실 왕복, 전체 스키마와 다른 OWPML 버전 지원도 남아 있다. Rust 판의 HTML 뷰어를 신규 Zig 기능으로 세지 않는다는 이전 원칙은 그대로다.