Skip to content

feat(layout): estimate pages on a line grid, after a section break and with notes beneath the text or at each section's end - #555

Merged
airmang merged 3 commits into
mainfrom
feat/p8a-estimate-support
Oct 24, 2026
Merged

airmang merged 3 commits into
mainfrom
feat/p8a-estimate-support

Conversation

@airmang

@airmang airmang commented Oct 23, 2026

Copy link
Copy Markdown
Owner

요약

쪽 수 추정(estimate_pages)이 지금까지 지원 밖으로 두던 네 경우를 한/글 2024처럼 따릅니다.

  • 뒤 문단의 구역 설정: 첫 문단이 아닌 문단에 hp:secPr가 있으면 한/글처럼 그 문단에서 새 구역이 새 쪽으로, 그 설정으로 시작합니다.
    첫 문단에 구역 설정이 없으면 그 앞 문단들은 한/글 기본 용지(A4 59528×84188, 기본 여백, 한 단)에 놓입니다.
  • 줄 격자(hp:grid@lineGrid): 격자에 맞추는 문단(snapToGrid)은 줄 높이·줄 간격과 문단 위아래 간격이 각각 격자 칸 단위로 올림되고,
    첫 줄은 놓일 자리에서 다음 격자 줄로 내려갑니다. 쪽 끝은 줄 윗변과 올림한 줄 높이로 판정합니다. 글자 격자(charGrid)는 모든 문단에
    한/글 줄 배치 정보가 있을 때만 따릅니다. 격자 구역에 표·개체·주석이 있거나 단이 여럿이면 계속 지원 밖입니다.
  • 본문 아래 각주(beneathText): 쪽 아래 각주와 같은 자리를 차지합니다.
  • 구역 끝 미주(END_OF_SECTION): 그 구역 본문 뒤, 다음 구역 쪽 앞에 놓입니다. 미주의 beneathText는 배치를 바꾸지 않습니다.

함께 고친 것:

  • 미주의 마지막 줄 아래 줄 간격을 세지 않습니다. 한/글은 한 줄 미주를 글 높이만큼씩 붙여 쌓습니다(여러 줄 미주의 안쪽 줄 간격은 셉니다).
    미주가 많은 문서의 쪽 수가 전보다 적게 나올 수 있습니다.
  • (실험) write_line_caches는 뒤 문단에 구역 설정이 있는 구역에 줄 배치 정보를 쓰지 않습니다. 그 구역은 다른 폭으로 줄을 나누기 때문입니다.

시험

  • 한/글 2024 데스크톱이 배치해 저장한 합성 문서 10개를 픽스처로 더했습니다(작성자·마지막 저장자·날짜는 비움). 각 시험은 저장본 그대로와
    줄 배치 정보를 지운 사본 모두에서 추정 쪽 수와 본문 줄 위치가 한/글 저장본과 같은지 봅니다.
    • 구역 설정 픽스처는 한/글이 저장하며 두 구역으로 나눈 문서를, 시험에서 한 구역으로 다시 합쳐 입력을 만듭니다.
  • 지원 밖을 고정하던 시험 셋(구역 끝 미주, 본문 아래 각주, 줄 격자로 지원 밖을 만들던 줄 캐시 시험)을 새 규칙에 맞게 바꿨습니다.
  • 줄 캐시: 뒤 문단에 구역 설정이 있는 구역에 아무것도 쓰지 않는 시험을 더했습니다.

🤖 Generated with Claude Code

airmang and others added 3 commits October 24, 2026 08:11
…d with notes beneath the text or at each section's end

Section settings in a later paragraph start a new section on a new page (paragraphs before the first settings
go on Hancom's default A4 page). In a section with a line grid, a paragraph snapping to it takes whole grid
steps for each line's height and advance and for its spacing; a character grid is followed through layout
caches. Footnotes beneath the text take the room they take at the page foot, endnotes at each section's end
follow that section's text, and a note's last line takes no line spacing below it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ings to Hancom in write_line_caches

The page estimate now follows such a section as the sections Hancom starts there, but its lines would break at
the first settings' width: no cache is written in it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s the page estimate follows

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@airmang airmang left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

head d49ef93 / base 5c345e5ac5fc9a1885c7867958e18345a963e8fb를 검토했습니다. 구역 분할의 복사/번호 라벨 연결, 격자 올림과 캐시 조건, 각주/미주 쌓기 및 관련 호출 경로를 확인했습니다.

해당 head의 CI13개 성공 및 Python 3.12 로그의 7,079 passed / 18 skipped / 1 xfailed, coverage90.11%, Ruff·mypy·pyright 통과를 확인했습니다. 별도 체크아웃의 FormFit 규칙·line-cache·page-estimate 1,723개도 통과했습니다. 격자 fixture 2개에서 작성된 캐시75개는 저장된 Hancom 속성과 일치했습니다. 다만 아래 미주 조합은 기존 테스트에 없어 추가 재현에서 실패했습니다.

미해결 P2: 마지막 구역의 END_OF_SECTION 미주와 이전 구역의 END_OF_DOCUMENT 미주를 함께 두면 두 그룹이 같은 본문 끝 좌표부터 배치됩니다(인라인 설명/재현 참조). 이를 수정하거나 그 조합을 unsupported로 남기는 회귀 검사가 필요합니다. 현재 GitHub 충돌도 있어 병합은 보류합니다. 자기 PR이므로 COMMENT로 남깁니다.

Comment thread src/hwpx/layout/pages.py
try:
layouts[-1] = _with_endnotes(measure, layouts[-1], _note_shape(root.sections[-1].element, "endNotePr"),
notes)
layouts[-1] = _with_endnotes(measure, layouts[-1], _note_shape(part, "endNotePr"), notes)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 구역 끝 미주 뒤에서 문서 끝 미주를 이어서 배치해야 합니다. 위 314행에서 마지막 구역의 own 미주를 이미 _with_endnotes로 붙여도 이 함수는 frames만 늘리고 layout.end는 본문 끝 그대로 둡니다. 따라서 이 두 번째 호출은 문서 끝 미주를 같은 좌표부터 다시 쌓고 max(frames, ...)만 취하여 두 영역이 겹쳐 계산됩니다. 정확한 head에서 HwpxDocument.new() → 첫 구역 본문에 add_endnote('note1', p) 200회(기본 END_OF_DOCUMENT) → add_section() → 둘째 구역 본문에 add_endnote('note2', q) 200회 → 둘째 구역 endNotePr/placement@place='END_OF_SECTION'로 재현했습니다. 어느 한 그룹만 200개여도 4쪽, 두 그룹을 모두 넣어도 unsupported=()와 4쪽입니다. 호출 추적도 첫 호출이 frames1→3, 두 번째가3→3이며 둘 다 end=(0,2600)에서 시작합니다. 구역 미주를 붙인 뒤의 커서를 유지해 문서 미주를 그 뒤에 놓거나, 검증 전까지 이 혼합 배치를 unsupported로 처리해 주세요.

@airmang
airmang force-pushed the feat/p8a-estimate-support branch from d49ef93 to a4a0652 Compare October 23, 2026 23:36
@airmang
airmang merged commit e9385e6 into main Oct 24, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant