HWPX 고치고 만들기
품앗이가 AI와 함께 일하며 남긴 기록을 다른 조직이 다시 쓸 수 있게 옮긴 글입니다. 묶음 전체와 도구를 만든 사람들은 한글 문서를 AI로 다루기에서 볼 수 있습니다.
요지
HWP 파일을 직접 고칠 수는 없지만, HWPX로 바꾼 뒤 고치면 사실상 편집할 수 있습니다. 이 길은 neolord0의 hwp2hwpx, airmang의 python-hwpx, mete0r의 pyhwp를 엮어서 열렸습니다. 다만 우리 기록에서 이 길의 첫 「성공」 보고는 절반만 맞았습니다. 본문 글자가 통과했다고 표까지 멀쩡하지는 않았고, 셀 너비나 도장 같은 세부는 라이브러리가 아니라 XML을 직접 만져야 했습니다.
1. HWP에서 HWPX로, 고친 뒤 다시 HWPX로
순서는 이렇습니다. hwp2hwpx로 HWP를 HWPX로 바꾸고, python-hwpx와 XML 처리 라이브러리로 내용을 고친 뒤, 한글 프로그램에서 엽니다. 기록에 따르면 한 지원사업 신청서의 이메일 칸 하나를 바꿔 한글 2020 이상에서 열었을 때 표와 체크 상자, 글꼴과 정렬이 원본 그대로였습니다.
중간에 걸린 자리가 둘 있었습니다. 첫째, 변환된 파일은 목록에 미리보기 글 파일이 있다고 적어 두고 실제로는 그 파일을 만들지 않아서 python-hwpx가 열기를 거부했습니다. 압축을 풀어 빈 미리보기 파일을 넣고, 형식 표시 파일을 압축하지 않은 채 맨 앞에 두어 다시 묶으니 열렸습니다. 둘째, python-hwpx의 글자 바꾸기 기능은 표 셀 안의 글자를 건너뛰었습니다. 그래서 본문 XML의 글자 노드를 직접 훑어 바꾸었습니다.
첫 「실증 성공」 보고는 절반이 거짓이었습니다. 우리 기록에 따르면 그 보고는 본문 글자가 통과한 것만 확인했고, 표와 줄 배치 정보는 표준에서 벗어난 채 남아 있었습니다. 그 파일로 낸 계획서에서는 표 셀이 위아래로 겹쳐 읽을 수 없었고, 마감 시간을 일부 깎아 먹었습니다. 받는 사람이 그 파일을 열 프로그램이 없다는 것을 알면서 「완료」를 붙인 것도 잘못이었습니다.
그 뒤로 정한 규칙은 셋입니다. 고친 HWPX는 rhwp의 덤프와 hwpxctl의 검증 명령으로 스스로 점검합니다. 한컴 오피스나 rhwp를 엔진으로 쓰는 HOP으로 렌더해 보기 전에는 완료라고 말하지 않습니다. 사람에게 넘길 때는 HWPX만 주지 않고 PDF 변환본을 같이 줍니다.
2. 표 셀 너비는 XML에 직접 적는다
python-hwpx로 표를 새로 넣으면 모든 셀이 같은 너비로 들어가고, 너비를 정하는 기능은 없었습니다. 기록에 따르면 사업계획서 표가 전부 2.54cm 균등 칸으로 들어갔습니다. 해결은 XML의 셀 크기 값에 너비를 직접 적는 것이었습니다. 한글 문서의 길이 단위로 1cm는 약 2,834.6입니다.
한글 프로그램의 기본 정렬인 양쪽 정렬이 낱말 사이를 벌려 놓으면 문단 정렬을 왼쪽으로 바꿉니다. 검증 명령을 통과했다고 화면이 맞는 것은 아니어서 한글에서 열어 봐야 했습니다. 셀 너비를 잡은 뒤에도 글꼴 크기, 줄 간격, 셀 여백 같은 세부가 남았는데, 그런 마무리는 한글 프로그램에서 사람이 직접 손보는 편이 가장 짧은 길일 때가 있다고 그 기록은 적습니다.
3. 양식을 채우고 도장을 찍는다
한 연합회의 강사비 수령증 양식을 채운 기록이 있습니다. 빈칸 글자는 1절의 왕복 경로로 채웠고, 도장 그림을 넣는 방법은 새로 확인해야 했습니다.
그림은 새로 만들지 말고 문서에 이미 있는 그림 요소를 복제합니다. 그 양식에는 연합회 직인 그림이 이미 들어 있었습니다. 그 요소를 통째로 복사한 뒤 가리키는 이미지만 새 도장으로 바꾸는 쪽이 압도적으로 안전했습니다. 그림 요소에는 위치, 원래 크기, 현재 크기, 회전, 확대 행렬, 자르기, 여백 같은 하위 항목이 열 개가 넘어서 손으로 쓰면 반드시 무언가를 빠뜨립니다. 바꿔야 하는 것은 고유 번호, 크기, 회전 중심, 확대 행렬, 위치 정도였습니다. 새 이미지는 문서 꾸러미의 목록 파일에 한 줄로 등록하면 됐고, 투명 배경 도장도 그대로 살았습니다.
글자 길이를 바꾸면 절대 위치에 놓인 그림이 어긋납니다. 「서명」 앞의 빈칸에 날짜와 이름을 넣자 「서명」 글자가 오른쪽으로 밀렸는데, 도장은 절대 좌표라 따라오지 않았습니다. 그래서 넣는 글자의 폭만큼 공백을 빼서 줄 전체 폭을 지켰습니다. 기록에 따르면 16포인트에서 한글 한 자는 16픽셀, 공백은 8픽셀, 숫자는 8.8픽셀이었습니다. 숫자를 공백과 같은 폭으로 잡으면 몇 픽셀씩 어긋납니다.
고친 HWPX는 당시 쓰던 렌더 도구로 그릴 수 없었습니다. 그래서 원본 HWP를 SVG로 그리고, 그 그림 위에 HWPX에 적은 값과 똑같은 계산식으로 도장 그림을 얹어 보았습니다. 같은 식으로 얹었으니 진짜 배치 검증이 되었고, 눈으로 위치를 확정했습니다.
그 수령증의 주민등록번호와 계좌번호 칸은 채우지 않았습니다. 나머지는 다 넣고 그 두 칸만 비워 사람에게 넘겼습니다. 문서에 적혀 밖으로 나가면 되돌릴 수 없는 값이라 사람 손을 한 번 거치게 둔다고 그 기록은 적습니다.
4. HWPX를 글로 꺼낸다
HWPX는 압축 파일이고 본문은 Contents 폴더의 section 번호 XML에 들어 있습니다. 판마다 XML 이름공간이 달라서 태그를 비교할 때는 이름공간을 뺀 로컬 이름으로 비교해야 합니다. 이렇게 하면 한글 프로그램 없이도 본문을 문단 단위로 꺼낼 수 있었습니다.
우리 서버에서 실측한 형식별 경로는 이렇습니다.
| 형식 | 되는 길 |
|---|---|
| PyMuPDF로 한글까지 깨끗하게 뽑힌다 | |
| DOCX | pandoc으로 마크다운이 된다 |
| HWPX | 압축을 열어 XML을 직접 읽는다 |
| HWP 3.0 같은 구형 | 길이 없다. 다시 저장해 달라고 부탁한다 |
스캔한 PDF는 글자 추출이 빈 문자열을 돌려줍니다. 빈 결과를 그냥 통과시키면 내용 없는 노드가 생기므로, 빈 결과는 명시적으로 걸러 냅니다.
처방 정리
- HWP를 고칠 때는 HWPX로 바꿔 고치고, 표 셀 안 글자는 XML을 직접 훑어 바꾼다.
- 고친 파일은 덤프와 검증 명령으로 점검하고, 렌더해 보기 전에는 완료라고 말하지 않는다.
- 표 셀 너비는 XML의 셀 크기 값에 직접 적는다.
- 도장은 문서에 있는 그림 요소를 복제해 넣고, 그 줄의 글자 폭을 지킨다.
- 주민등록번호와 계좌번호처럼 되돌릴 수 없는 값은 사람에게 넘긴다.
도구 메모
- HWP를 HWPX로: hwp2hwpx 저장소를 받아 빌드한 뒤, 읽기와 쓰기 두 줄을 부르는 짧은 자바 실행 파일을 만들어 쓴다.
- HWPX 다시 묶기: 형식 표시 파일 mimetype을 무압축으로 첫 항목에 넣는다.
- 셀 너비: 표의 각 셀에서 셀 주소의 열 번호를 읽고 셀 크기의 width 값에 센티미터 곱하기 2834.6을 적는다.
- 그림 등록: Contents 폴더의 content.hpf 목록에 이미지 항목 한 줄을 넣는다. 그림 좌표 단위는 1인치가 7200이라 96dpi 화면 1픽셀이 75다.
- 점검:
rhwp dump와hwpxctl validate. 리눅스에서 보려면 HOP 배포본을 설치한다. - 작업 스크립트 이름을 inspect.py로 짓지 않는다. 파이썬 표준 모듈과 이름이 겹쳐 XML 라이브러리가 엉뚱한 오류로 죽는다.
관련 노드
- 허브: 한글 문서를 AI로 다루기
- HWP 읽기: 표 안 글자를 잃지 마라
- 워드, 구글 문서, PDF로 낼 때
- AI가 만든 문서의 완료 판정
- AI와 함께 정책문서 교차검증하고 한글문서 자동수정하기: HWPX를 AI가 직접 고친 교정 절차입니다.