Skip to content

fix(layout): keep an empty paragraph's line above its object offset down - #429

Open
airmang wants to merge 8 commits into
mainfrom
fix/page-estimate-empty-paragraph-band
Open

airmang wants to merge 8 commits into
mainfrom
fix/page-estimate-empty-paragraph-band

Conversation

@airmang

@airmang airmang commented Oct 6, 2026

Copy link
Copy Markdown
Owner

바뀐 점

쪽 수 추정(실험, estimate_pages)에서, 개체 하나만 든 빈 문단에 그 개체가 위아래 배치로 문단 위에서 아래로 띄워져 있을 때 한/글처럼 놓는다. 전에는 그 문단을 '띄운 거리 + 개체 높이'만큼 높은 한 줄로 세어, 뒤의 모든 줄을 개체 아래로 보냈다.

한/글의 동작

  • 빈 문단의 줄은 개체가 어디 있든 제자리에 있다. 개체의 띠가 그 줄의 아래끝보다 위에서 시작하면, 그 줄은 폭 0인 빈 조각이 된다.
  • 뒤 줄은 보통 간격으로 놓이다가, 띠와 겹치는 첫 줄부터 띠 아래끝으로 간다.
    • 겹침은 줄 위쪽이 띠 아래끝보다 위이고, 줄 아래끝(줄 간격은 빼고)이 띠 위쪽보다 아래인 경우다.
    • 띠 아래로 간 첫 줄은 띠 아래끝에 놓이고, 그 뒤는 보통 간격으로 이어진다.
  • 표와 그림이 같다.
  • 공백 하나라도 든 문단은 글 문단이라 그 줄 자체가 띠와 겹치면 띠 아래로 간다(전과 같음).

고치기 전

빈 문단 위에서 3000·10000·18196 아래에 둔 6000 높이의 표나 그림 뒤에서는, 띠 위에 들어갈 줄들까지 개체 아래로 갔다. 띄운 거리가 줄 하나보다 작으면 뒤 줄이 어차피 띠에 닿아, 결과가 한/글과 같았다.

코드

layout/pages.py

  • _pushing_object가 개체 하나만 든 빈 문단(글·부호·각주·머리 번호 없음)도 받는다(bare). 띄운 거리 0인 개체는 전처럼 문단의 줄이다.
  • 이 규칙은 개체의 띠가 그 문단 줄의 간격(줄 높이 + 줄 간격) 아래에서 시작할 때만 쓴다(_clear_of_its_band). 그보다 위에서 시작하면 다음 줄이 어차피 띠에 닿아, 전처럼 개체를 문단의 줄로 놓아도 결과가 같다.
    • 그런 문단은 쪽을 넘는 흐르는 표처럼 전 방식이 이미 다루는 경우를 그대로 둔다.
  • _Para.bare: 그런 문단의 빈 줄은 자기 개체의 띠에 닿아도 제자리에 둔다. _push(그림 등)와 _Paginator._banded(흐르는 표)가 그 줄을 띠에서 뺀다.
  • 흐르는 표는 홀로 둔 표처럼 문단의 앞 간격 위 문단 위쪽부터 띄운 거리를 잰다.
  • 모듈 설명과 변경 로그를 고친다.

테스트

tests/test_layout_page_estimate.py의 HANCOM_PAGES에 한/글 저장본 열아홉을 더한다. 캐시가 있을 때와 없을 때 모든 줄 위치와 쪽 수를 시험한다.

  • 공통 꼴: 10pt 160% 글 3줄, 넷째 줄인 빈 문단에 6000 높이의 표나 그림을 d 아래에 둠, 뒤에 글 30줄
  • 빈 문단: 표·그림 각각 d = 0, 500, 1000, 1600, 3000, 10000, 18196. 표 18196에 글 60줄로 쪽을 넘는 판도 있다.
  • 공백 문단(대조): 표 0·500·3000, 그림 3000

고치기 전에는 d = 3000·10000·18196인 일곱 판이 두 모드에서 틀렸다. 나머지는 고치기 전에도 맞고, 규칙의 경계를 지킨다.

이 PR은 #414 위에 쌓았다. #414가 병합되면 그 커밋들은 이 PR의 diff에서 빠진다.

🤖 Generated with Claude Code

airmang and others added 8 commits October 6, 2026 05:20
… and set as characters

Some paragraphs holding both objects placed top and bottom and objects set as
characters were left unsupported. Several placed top and bottom from an empty
paragraph's top now stack, and the line of the objects set as characters goes
below them, whatever the order of the runs. One placed below the lines of a
paragraph of text is followed with objects set as characters among the text: its
lines stay, the lines reaching it go below its foot, and a line that goes on to
the next page leaves it on the first. One placed up from an empty paragraph's top
stands at the top, where Hancom saves it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…below its line

A paragraph holding only spaces and an object placed top and bottom down from
its top was taken as empty: its line stayed at its top. Hancom takes even one
space as text, so the line goes below the object when it reaches it, as with
any text; only an empty paragraph keeps its line at its top.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p for real

The fixture of a table placed up from its paragraph's top now holds the offset
as Hancom keeps it (unsigned), so it tests that case itself; the test that put
the offset back into the old fixture goes. Hancom draws such a table up there
but lays the line out below it as if it stood at the top.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t a drawing

Hancom lays a table placed top and bottom up from its paragraph's top out, and
draws it, at that top: the offset counts as 0. A drawing keeps its place there,
and its paragraph's line goes elsewhere, so an empty paragraph of objects set as
characters with a drawing placed up stays unsupported. Six tables placed up from
an empty paragraph, saved by Hancom, pin the rule.

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

A paragraph holding a line of text and a flowing table placed below it, right
after another such paragraph, was left unsupported. Hancom starts the second
paragraph at the foot of the first table (of its part on the next page when it
went on over the page end), places its own table from its top as usual, and
starts the text after at that table's foot. A second paragraph whose first line
does not reach the first table stays unsupported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An object placed top and bottom up from its paragraph's top has its offset
kept unsigned. A paragraph of text or spaces took such an object as one pushing
its lines from four billion units down; it stands at the paragraph's top
instead (a table: see the table rule), as in a paragraph of objects set as
characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A table or picture placed top and bottom down from an empty paragraph
holding nothing else: Hancom keeps the paragraph's empty line where it
is (an empty piece where the object's band starts above its foot) and
moves only the lines after it that reach the band below it, the first of
them to the band's foot. The page estimate counted such a paragraph as
one line as tall as the offset and the object, so every line after it
went below the object. When the object's band starts below the
paragraph's line (a table measured from the paragraph's top above its
spacing, as a table alone), the paragraph is now laid out as a pushing
object of a paragraph of text, its own line staying; otherwise the next
line reaches the band anyway and the paragraph is laid out as before.

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

This branch has not been deployed

No deployments
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