Repository navigation
fix: #8149 빈 host 자리차지 표의 저장 상자·쪽 바닥 첫 조각 계약 (사양서 74→71쪽) - #8184
Open
planet6897 wants to merge 25 commits into
Open
planet6897 wants to merge 25 commits into
planet6897 wants to merge 25 commits into
Conversation
planet6897
force-pushed
the
fix/8149-empty-host-float-outer-top
branch
2 times, most recently
from
October 26, 2026 02:57
5c80b5d to
567ff9d
Compare
jangster77
self-requested a review
October 26, 2026 14:19
…인정한다 (edwardkim#8149) `from_stored_ladder` 는 다음 저장 줄이 닫는 빈 host 자리차지 표의 상자를 조판·배치에 공유한다 (`다음 − host = 위 여백 + 선언 높이 + 아래 여백 − host 앞 간격`). 그런데 캡션이 있는 표를 통째로 거절해, 아래 캡션이 붙은 표는 옛 통째 배치로 떨어졌다. 그 경로는 표를 바깥 위 여백 없이 그리고 표 뒤 흐름 끝을 다음 문단의 저장 줄에 둔 뒤 그 문단이 위 간격을 또 더해, 표마다 흐름이 6.7px 씩 내려갔다(한글문서파일형식 39쪽, 마지막 행이 다음 쪽으로 넘어가 74쪽). - 아래 캡션은 `캡션 간격 + 저장 캡션 줄` 을 선언 높이에 더한다(표 235: 10545 = 566 + 6193 + 852 + 950 + 1984). 위·왼쪽·오른쪽 캡션은 본체 원점·폭이 다른 기하라 계속 거절한다. - 앞 항목이 자리차지 표 상자로 끝난 host 는 한/글이 앞 간격을 두지 않는다. 사다리가 `다음 − host = 상자` 로 그것을 증명하면 같은 상자로 받는다(표 235: 앞은 표 234 조각). 두 경우 모두 다음 저장 줄이 상자 바닥이고, 후속 문단은 기존 `stored_frame_successor_shared_spacing` 계약으로 그 줄에 앞 간격을 한 번만 소비한다. 결과(devel 605ccfd → 이 커밋, 96dpi, 정본 PDF 좌표): - 표 235 251.7 (244.2→, 정본 251.7) · pi=236 384.8 (391.4→, 384.6) - 표 239 500.9 (500.0→, 500.5) · pi=240 606.2 (619.5→, 605.8) - pi=246·254 가 쪽을 나누지 않고 74 → 73쪽(정본 71쪽, 47·50쪽 부근 +1 두 번은 edwardkim#7735) 남은 범위: 다음 문단에 저장 줄이 없는 경우(40쪽 표 250·254 뒤 「4.3.x」 제목)는 사다리로 상자를 인증할 수 없어 옛 경로에 남는다 — 정본 40쪽 끝 pi=256 은 아직 41쪽으로 넘어간다.
`델타 = 상자` 는 host 앞 간격이 흡수됐다는 사다리의 증언이지만, 원점(조판 커서)이 그 저장 줄에 있다는 증거는 아니다. 156160455 7쪽 pi=71 은 글이 있는 host(pi=70)의 자리차지 밴드 뒤라 커서가 host 글줄 끝(174.8)에 있고 저장 줄은 밴드 바닥(296.8)이다. 첫 커밋은 이 경우도 받아 표를 앞 표와 겹쳐 그렸다(issue_6797 회귀). `from_empty_stored_ladder_host` 에 `origin_closes_float_frame` 을 두어, 바로 앞 조판 항목이 앞 문단(글 없는 host)의 자리차지 표 상자일 때만 흡수 등식을 받는다. 조판 블록 경로만 이를 확인할 수 있으므로 거기서 넘기고, 셀 안·lane 경로는 종전 등식만 쓴다.
…#8149) 한글문서파일형식 정본 PDF·저장 사다리로 확정한 한/글 규칙(빈 host 문단기준 자리차지 표): 표 원점 = host 문단 위끝 + 바깥 위 여백, 다음 문단의 앞 간격은 바깥 아래 여백 안에서 소비. 저장 사다리가 상자를 인증하지 못하는 경우(다음 문단에 저장 줄이 없는 「4.3.x」 제목 등)에도 같은 규칙을 공유 결과로 낸다. - 후속 문단 앞 간격: `empty_host_float_successor_absorbed_spacing_px` — 바로 앞 조판 항목이 글 없는 host 의 세로 오프셋 0 자리차지 표(끝 조각)이면 `min(앞 간격, 바깥 아래 여백)` 을 조판(flow.rs)과 배치(layout.rs)가 같은 자리에서 소비한다. 인증 배치가 있으면 기존 `stored_frame_successor_shared_spacing_px` 가 맡는다. - 표 원점: `from_saved_whole_empty_host` 가 저장 단 원점이 없을 때 조판 커서(문단 위끝) + 적용한 앞 간격을 원점으로 쓴다(세로 오프셋 0 한정 — 양수 오프셋은 edwardkim#4068 계약). - 캡션 높이: 글자 없이 표 하나만 든 host 는 캡션에 host 줄간격을 더하지 않는다(저장 줄 폭이 0 이 아니어도 — 30쪽 표 185 의 폭 39628). 결과(정본 쪽마다 글줄 위치 대조, |중앙값|>2 또는 |최대|>3px 인 쪽): 48 → 33, 전체 73 → 71쪽 (정본 71). 13쪽 표 51(오프셋 경로)·쪽 바닥 행 분할은 남는다.
…m#8149) 한/글은 쪽 바닥에서 나뉘는 빈 host 문단기준 자리차지 표의 첫 조각 아래에 그 표의 바깥 아래 여백과 (아래 캡션이면) 캡션 자리(간격 + 캡션 줄 높이)를 남긴다. 한/글 2020 저장·Print 합성 `samples/issue8149/gen_rowfit.py` 16사례(캡션 유무 × 아래 여백 1984/0 × 행 높이 4종)로 확정: 캡션 없음·여백 0 은 본문 끝까지, 여백만 있으면 여백, 아래 캡션이면 여백 + 캡션. rhwp 는 저장 첫 조각 프레임(선언 개체 높이 = 첫 조각, edwardkim#7147)이 있으면 그 높이로 같은 컷을 얻었지만, 저장 근거가 없는 쪽(한글문서파일형식 38쪽 표 234 등)에서는 아무것도 남기지 않아 한 행씩 더 받았다. - 저장 근거가 없을 때 첫 조각 예산에서 `바깥 아래 여백 + 아래 캡션` 을 뺀다(확정 배치가 예산을 다시 정하는 갈래 포함). - 저장 첫 조각 프레임의 앵커 대조: 저장 앵커는 host 줄 위치(앞 간격 뒤)이므로 흐름 원점(문단 위끝)에 앞 간격을 더해 견준다 — 종전에는 이 차이(6.67px)로 프레임을 버렸다. - 선언 개체 높이가 행 경계에 1px 안으로 적중하면 그 행 끝을 첫 조각 상한으로 쓰고(edwardkim#7147 판별), 저장 프레임이 인정되면 위 계산 예약은 쓰지 않는다. 합성 16사례 모두 한/글과 같은 행에서 끊고, 한글문서파일형식 정본 대조 어긋난 쪽 33 → 28.
…ardkim#8149) - `from_empty_reflow_host`: 저장 원점은 host 줄 위치(문단 앞 간격 뒤)다. 한/글은 문단 기준 표를 문단 윗변 + 바깥 위 여백에 두므로(edwardkim#7620, `from_saved_whole_empty_host` 와 같은 규칙) 적용한 앞 간격을 뺀 자리에서 상자를 세운다. 한글문서파일형식 13쪽 표 51(나누지 않음)이 +6.7px 아래였다. - 분할 진입 검사: 첫 줄은 문단 앞 간격 뒤에 놓인다. 분할 루프 예산은 그 간격을 빼는데 진입 검사만 빼지 않아, 루프가 면제하는 조각 첫 줄(edwardkim#6568)이 본문 바닥을 넘어도 그 쪽에 남았다 (13쪽 pi=53: 남은 16.43 − 앞 간격 6.67 = 9.76 < 첫 줄 13.33 — 한/글은 다음 쪽). drift 안전 여백은 여기서 빼지 않는다(edwardkim#6568 주석의 이중 차감 회귀). 한글문서파일형식 정본 대조 어긋난 쪽 28 → 27, 악화 0. 합성 rowfit 16사례 유지. nextest 11352 통과.
…dwardkim#8149) 앞 항목이 TAC 표면 배치는 저장 vpos 보정을 건너뛰고, 다음 TAC 표 host 의 위 간격은 edwardkim#5809 규칙으로 직접 더한다. 그 규칙이 HWPX 저장본(또는 저장 줄 없는 문단)에만 걸려 HWP5 저장본에서는 위 간격이 빠졌다. HWP5 저장 사다리도 그 간격을 둔다 — 한글문서파일형식 25쪽 pi=166→167: 5438 + 15368 + 600 + 500 = 21906. 조판은 이 간격을 이미 전진에 넣으므로(조판 커서 285.5 = 저장 문단 상단 285.4) 배치만 표마다 6.67px 씩 위로 누적됐다. 정본 대조: 25쪽 −19.4 → 0.7, 62쪽 −13.7 → −7.1, 악화 0(어긋난 쪽 27 → 26). nextest 11352 통과.
…im#8149) - TAC 표: 배치가 표 상단을 «줄 상단 + om_top» 에 둘지 정하는 edwardkim#5729 갈래에서, 저장 밴드가 `om_top + 선언 높이 + 아래 캡션(간격 + 저장 캡션 줄) + om_bottom` 인 경우도 바깥 상자로 인정한다(`tac_stored_band_is_captioned_outer_box`). 한글문서파일형식 62쪽 표 397: 9363 = 566 + 5011 + 1802 + 1984 — 종전 om_top 만큼 위(−7.1px). 조판의 후속 원점 판정 (`tac_stored_band_is_outer_box`)은 캡션이 섞인 줄을 표 원점으로 쓰지 않는 계약이라 그대로 둔다(넓히면 rowbreak_short_row_whole_carry 컷 14건이 바뀐다). - TAC 도형 host: 다음 저장 줄이 `줄 + 줄간격` 만큼 떨어질 때만 꼬리 줄간격을 복원했다. 사다리 경계는 `줄 + 줄간격 + 이 문단 아래 간격 + 다음 문단 위 간격` 이므로 그 등식도 받는다 (21쪽 pi=140: 18340 + 15255 + 628 + 700 + 500 = 35423, 종전 다음 문단 −8.0px). 정본 대조 어긋난 쪽 26 → 24, 악화 0. nextest 11352 통과. `stored_bottom_caption_hu` 를 공용으로 연다.
저장 줄 스냅이 이미 같은 앞 간격을 되돌린 문단(한글문서파일형식 41쪽 문단 262, 51쪽 문단 328)에서 empty_host_float_successor_absorbed_spacing_px 를 다시 빼 6.7px 위로 그렸다. 소비량을 스냅 전 흐름 끝(상자 바닥) 기준으로 재어, 조판이 정한 문단 시작과 같게 한다.
- 빈 host 문단 기준 자리차지 표의 첫 조각 바닥 예약(바깥 아래 여백 + 아래 캡션)을 empty_host_float_first_fragment_reserve_px 하나로 모아 행 예산(budget)과 첫 행 이월 판정(prepare)이 함께 쓴다. 선언 개체 높이가 첫 조각만 덮는 표(edwardkim#7147, 한/글이 거기서 나눈 저장 증거)는 이월 판정에 쓰지 않는다. 한글문서파일형식 32쪽 표 199(선언 = 7행 전체)는 제목 행만 남기지 않고 33쪽으로 통째로 넘어간다. 51쪽 표 334(선언 = 제목 행)는 그대로 나뉜다. - 통째 첫 조각 갈래가 흐름을 끝 조각과 같은 점유(아래 캡션·바깥 아래 여백 포함)로 닫는다. 넘긴 표 199 뒤 제목이 캡션에 겹쳤다. - 앞 쪽에서 포맷한 표를 이월해 새 단 첫머리에서 열면 host 앞 간격을 빼고 바깥 위 여백만 쓴다(HostSpacing.column_top_before). 42쪽 표 270 뒤 표 271 이 6.7px 내려갔다. 한/글 2020 합성(gen_headerrow.py 15사례: 제목 줄 반복 × 제목 칸 × 채움)은 수정 전후 모두 일치하고, 제목 행만 남기는 분할 자체는 한/글도 허용함을 보였다(H03). 행 분할 합성 16사례 유지. 정본 대조 어긋난 쪽 22 → 19.
- 저장 원점으로 세우는 빈 host 표 상자(from_saved_whole_empty_host)의 꼬리에서 글 없이 표 하나만 든 host 의 줄간격을 뺀다. 저장 사다리는 다음 빈 host 표의 줄을 `표 + 캡션 + 바깥 아래 여백` 바닥에 둔다(한글문서파일형식 33쪽 표 202→203: 다음 줄 vpos 45542 = 상자 바닥). 같은 문서의 저장 사다리 경로 (52쪽 표 335~337)는 이미 이 상자를 썼다. 30쪽 표 185 캡션(5a2838e62)과 같은 규칙이다. - host 문단 앞 간격을 표를 실제로 여는 자리에서 정한다. 앞 쪽에서 포맷한 표를 선언 높이 이월로 새 단 첫머리에 두면 바깥 위 여백만 쓴다(47쪽 표 297, 6.7px). 캡션 RowBreak 단 배치·저장 사다리 없는 빈 앵커·재조판 host 상자가 같은 값을 쓴다. 정본 대조 어긋난 쪽 19 → 12(33·44~49쪽 일치). 행 분할 합성 16사례 유지.
단 첫 항목이 글 없는 host 의 문단 기준 자리차지 표면 쪽의 저장 vpos 원점을 host 첫 줄 vpos 로 잡았다. 그 줄은 문단 앞 간격 뒤에 있는데 표 상자는 문단 위끝에서 바깥 위 여백만큼 내려 시작하므로, 그 쪽의 저장 앵커가 모두 앞 간격 만큼 위로 읽혔다. 한글문서파일형식 42쪽은 표 270 줄 500 을 원점으로 써 표 273 의 저장 첫 조각 프레임(선언 = 첫 조각 234.3px, edwardkim#7147)을 앵커 불일치로 버렸고, 한/글보다 한 행 적게 실어 43쪽부터 16px 밀렸다. 저장 사다리가 다음 줄을 `원점 + 위 여백 + 높이 + 아래 캡션 + 아래 여백` 에 두고(270→271: 0 + 566 + 14476 + 1802 + 1984 = 18828) host 줄이 표 위 바깥 여백 띠 안에서 시작하면 그 원점을 쓴다. 세로 오프셋이 있는 표(쪽을 넘기면 오프셋을 버림, edwardkim#4068)와 host 줄이 표 아래인 경우(issue7198)는 제외한다. 조판(stored_tac)과 배치(layout)가 같은 함수를 쓴다. 정본 대조 어긋난 쪽 12 → 10(42·43쪽 일치). nextest 11356 통과.
- 합성 3종(samples/issue8149/gen_rowfit·gen_reservefit·gen_headerrow, 한/글 2020 저장 HWP 와 Print PDF): 사례마다 쪽별 행 소속을 PDF 본문 글자에서 읽은 기대값과 대조한다. 한/글 저장본은 저장 첫 조각 프레임 경로로도 맞아 수정 전에도 통과한다 — 쪽 바닥 예약(바깥 아래 여백 + 아래 캡션), 제목 행만 남기는 분할, 통째 이월 계약의 보호용이다. - 사양서(한글문서파일형식 5.0, 정본 71쪽): 표 199 는 33쪽에만(선언 = 7행 전체), 표 334 는 51·52쪽에 나뉜다(선언 = 제목 행). 수정 전은 74쪽으로 쪽수부터 다르다. 전쪽 실루엣(raw): 사양서 Native 최저 93.80 · WASM 94.14, 합성 4종 100%.
empty_host_float_successor_absorbed_spacing_px 가 저장 조판 프로필에서 다음 문단의 저장 첫 줄 vpos 를 본다. «상자 끝 + 앞 간격» 과 ±1px 로 맞고 «상자 끝 − 흡수 + 앞 간격» 과는 다르면 한/글 출력이 앞 간격을 소비하지 않은 것이므로 흡수하지 않는다. 조판(flow.rs)과 배치(layout.rs) 가 같은 커서(본문 위끝 기준)로 판정한다. samples 에서 저장 줄과 맞출 수 있는 흡수 사례 118곳 중 117곳은 저장 줄이 흡수한 위치를 증언하고, hwpspec 54쪽 문단 341(이어지는 마지막 조각 뒤 테두리 문단)만 흡수하지 않은 위치를 증언한다. 그 쪽 Native 79.56 → 89.24(devel 과 같음). 회귀: successor_keeps_spacing_when_its_stored_line_says_so — 수정 전 문단 341 첫 줄이 저장 줄보다 13.3px 위라 실패, 수정 후 통과. 흡수가 맞는 반례 hwpspec 53쪽 문단 337·사양서 55쪽 문단 349 도 저장 줄에 있다.
저장 조판에서 다음 문단의 저장 첫 줄이 있으면 «커서 + 앞 간격 − 저장 위치» 를 소비량으로 쓴다(0..=앞 간격, ±1px 는 0·흡수량·앞 간격으로 맞춤). 증언 범위 밖의 vpos 면 종전 규칙 min(앞 간격, 바깥 아래 여백) 이다. rowbreak-problem-pages 5쪽 pi=12: 저장 줄이 앞 간격 전부의 소비를 증언하는데 흡수량(1.9px)만 빼면 devel 의 stored_after_partial_table_shared_spacing_px 가 저장 줄과 맞추던 커서가 어긋나 문단이 11.5px 내려갔다(issue_7470 visible_successor_uses_the_original_line_origin… 실패). 이제 그 쪽도 저장 줄에 놓인다. hwpspec 54쪽 pi=341(소비 0)과 흡수 사례 117곳은 같다.
…#8149) 04b2cc3 의 바닥 예약(바깥 아래 여백 + 아래 캡션)은 한/글 2020 합성 gen_rowfit 16사례에서 얻었는데, 그 행은 모두 한 줄이라 통째로만 넘어간다. 첫 조각 행 예산에서 빼면 행 안에서 자르는 컷까지 쪽 끝을 못 쓴다 — 75544_pii_bunseok 65쪽 표 619(바깥 아래 여백 1.9px)는 한/글이 넷째 행을 쪽 끝까지 잘라 싣는데 rhwp 는 행 전체를 66쪽으로 넘겼다(issue_5846 cut_fragment_defers_partial_nested_tail_row 의 칸 (4,1) 소실). 첫 조각 예산(budget.rs)의 예약과 저장 프레임 인정 시 되돌림을 걷어내고, 첫 행이 통째로 들어가는지 보는 이월 판정(prepare.rs, 한글문서파일형식 32쪽 표 199)만 남긴다. 행 안 컷의 바닥 규칙은 한/글 오라클로 따로 확인한다.
7216d90 가 통째 첫 조각 갈래의 흐름 전진을 host_before_overhead + … 로 직접 합산했다. 그 합에는 단 맨 위 표의 바깥 위 여백이 들어가, paint 위치에만 쓰는 여백이 같은 쪽 다음 표의 흐름을 바꿨다(issue_4068 only_the_column_top_table_receives_outer_margin: 2912695 2쪽 첫 표 여백 0 이면 둘째 표가 1.88px 올라감). devel 의 partial_height + host_spacing_total 로 되돌린다. 이 갈래가 고치려던 한글문서파일형식 33쪽 표 199 뒤 제목은 Visual Sweep 으로 다시 확인하고, 필요하면 여백 계약을 지키는 방식으로 따로 다룬다.
d293089 은 통째 첫 조각 흐름 전진을 devel 의 partial_height + host_spacing_total 로 되돌렸다. 그러면 한글문서파일형식 33쪽 표 199(뒤 몫 0)가 아래 캡션 24.0 + 바깥 아래 여백 26.5 를 흐름에 싣지 않아 제목 200 이 캡션과 겹친다(사양서 33쪽 Native 54.56, hwpspec 47쪽 44.12). 7216d90 의 직접 합산은 반대로 host 간격 합의 뒤 몫(host 줄간격 등)을 버리고 단 맨 위 표의 바깥 위 여백을 흐름에 넣어 issue_4068 계약을 깼다. 흐름 전진 = 표 높이 + host 간격 합 + max(0, 끝 점유 − 합의 뒤 몫). 끝 점유는 아래 캡션 + 바깥 아래 여백 + 뒤 간격이다. 표 199 는 175.9(직접 합산과 같음), 2912695 2쪽 표 3 은 779.6(devel 과 같음). 사양서 33쪽·hwpspec 47쪽 회복, hwpspec 90 미만 2쪽(devel 과 같은 쪽).
…m#8149) 2c53026 가 첫 조각 예산에서 뺀 바닥 예약(바깥 아래 여백 + 아래 캡션)을 되돌린다. 한/글 2024 합성(행마다 세 줄인 1열 12행 빈 host 자리차지 표, 바깥 아래 여백 0/141HU × 바깥 위 여백 75HU 간격 24단계로 쪽 바닥 남는 틈을 1px 씩 줄임)에서 141HU(1.9px) 여백도 행 안 컷의 마지막 줄을 넘긴다 — 줄을 넘기는 문턱이 여백 0 보다 약 2px 앞이다. 행 안 컷에도 예약이 맞다(한글문서파일형식 33→34쪽 표 203: 예약 없으면 한 행을 더 싣는다). issue_5846(75544_pii_bunseok 65쪽 표 619)이 깨진 이유는 저장 되감김 첫 조각의 꼬리말 여유 (STORED_REWIND_FIRST_FRAGMENT_PAINT_FOOTER_GUARD_PX 4px)와 바닥 예약 1.88px 를 더해 뺀 것이다. 두 예약은 같은 쪽 바닥 띠를 남기므로 예약에서 여유만큼 뺀다. 한/글은 그 쪽에서 본문 끝 5.9px 위에서 첫 조각을 닫고 넷째 행(자식 첫 프레임 + 안 여백 29.87px)을 싣는다. 사양서 71쪽(정본 71) Native 최저 95.23 · fresh WASM 94.65, hwpspec 90 미만 2쪽(devel 과 같은 23·54쪽). issue_4068·5846·7470 과 edwardkim#8149 검사 통과.
devel 238de6d(edwardkim#8228)가 `from_stored_ladder` 를 저장본·재조판 공용 `from_ladder` 로 나눴다. 저장본 경로는 받은 `origin_closes_float_frame` 을 넘기고, 재조판 경로 (`from_reflowed_ladder`)는 앞 항목 상자 끝을 증언하는 호출자가 없으므로 `false` 다. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 실루엣 TSV head: 사양서·hwpspec·issue8149 합성 4종. devel: hwpspec(사양서는 devel 83쪽으로 쪽별 대조 불가). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
planet6897
force-pushed
the
fix/8149-empty-host-float-outer-top
branch
from
October 27, 2026 14:40
567ff9d to
ab8b419
Compare
Contributor
Author
최신 devel 로 rebase
|
같은 블록의 `first` 를 값으로 바인딩하는 변경(edwardkim#8205)과 함께 병합되면 `*first` 가 usize 역참조가 되어 컴파일되지 않는다. `&usize + usize` 도 usize 이므로 두 경우 모두 같은 값이다.
Contributor
Author
|
통합 검증(열린 PR 전체를 devel
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
한컴 「한글문서파일형식 5.0 revision1.3」(
samples/한글문서파일형식_5.0_revision1.3.hwp)이 devel 에서 74쪽(정본 PDF 71쪽)으로 나오던 문제를 고칩니다. 원인은 글 없는 host 문단의 문단 기준 자리차지 표(빈 host 표)를 둘러싼 여러 계약이 조판과 배치에서 어긋난 것입니다. 이 브랜치에서 71쪽 일치, 정본 대조 글줄 위치가 어긋난 쪽 48 → 10(남은 10쪽 중 실결함은 2쪽, 아래 「남은 범위」).Closes #8149 · 관련 #7735(47·50쪽 부근 +1 두 건이 함께 해소)
고친 규칙 (커밋 순)
18d868dd2델타 = 상자)도 인정gen_captioned_host.py한/글 20208be70c87afcdf1e7d5min(앞 간격, 바깥 아래 여백)만큼 흡수, 저장 원점 대체(세로 오프셋 0 한정), 단독 표 host 줄간격은 캡션에 안 더함89f600725바깥 아래 여백 + 아래 캡션을 남긴다. 선언 높이(#7147 첫 조각)가 행 경계에 맞으면 그 행이 상한gen_rowfit.py16사례 +gen_reservefit.py32사례(여백 200HU 간격, 예약 오차 0.1px)6556f971337a73d648·634c881891842eaa3cd2ea85186gen_headerrow.py15사례(한/글도 제목 행만 남기는 분할 허용 — H03)fdde0bcf227d782cd8조판과 배치가 같은 함수(
stored_table_column_origin,empty_host_float_first_fragment_reserve_px,empty_host_float_successor_absorbed_spacing_px)를 소비합니다.검증
f1c7a4ec6c(baseupstream/develdef947c). 정책 검사 base 도 def947c.cargo nextest run --cargo-profile release-test: 11356 / 11356 통과(새 4건 포함).rust-test-suite-manifest --check --base-ref def947c21통과. rustfmt 는 커밋된 blob 기준 통과.tests/cases/issue_8149_empty_host_float_page_bottom.rsspec_header_only_split_follows_declared_first_fragment— 사양서 71쪽, 표 199 는 33쪽에만, 표 334 는 51·52쪽에 나뉨. 수정 전(devel) 74쪽이라 실패.rowfit·reservefit·headerrow) 쪽별 행 소속 = 한/글 2020 PDF 본문 글자. 한/글 저장본은 저장 첫 프레임 경로로도 맞아 수정 전에도 통과 — 계약 보호용입니다.Visual Sweep (전쪽, 판정값
silhouette_raw_match_percent, 96dpi, 로컬 Windows)…-hancom-official.pdf(Hwp 2018)issue8149/captioned-host-2020issue8149/rowfit-2020issue8149/headerrow-2020issue8149/reservefit-2020devel 은 사양서 74쪽이라 쪽별 대조가 불가(전체 쪽수 불일치). 실루엣 보조값은 2px 이웃 관용 내용 실루엣이며 이진화·색상 경계 원값을 함께 보존했습니다(TSV 는 로컬
output/pr-review/8149/).대표 증적 (증적 head
567ff9d18, code headd878c2ad5, base238de6de4— 아래 「최신 devel rebase (238de6d)」)33쪽 — 표 199 를 32쪽 바닥에 제목 행만 남기지 않고 통째로 넘김, 뒤 표 203 의 저장 상자(raw Native 99.25% / WASM 99.88%)
43쪽 — 표 273 첫 조각이 저장 프레임대로 11행까지(raw 99.78% / 99.78%)
47쪽 — 이월된 표 297 이 쪽 첫머리에서 앞 간격 없이(raw 99.59% / 99.59%)
WASM review PNG 는 같은 폴더의
spec-p0NN-wasm-review.png.직접 판독
33·41·43·47쪽을 정본과 같은 배율로 대조: 표 상자 위치·쪽 소속·캡션·뒤 제목 위치 일치. 남은 차이는 표 바깥 테두리의 색·굵기(정본은 더 굵은 파란 외곽선)로 이번 변경 범위 밖입니다.
남은 범위 (미해결 · 미검증)
최신 devel rebase (2026-10-26, #8191 반영 devel)
기준
upstream/devel9418ed0c7, code headecb928662, 증적 head5c80b5d48(이전f1c7a4ec6). #8191 이 같은 축(빈 host 표 원점·각주 경계·물리 행 프레임)을 크게 바꿔, 단순 rebase 뒤 아래 회귀가 드러났고 각각 원인에서 고쳤습니다.layout.rs출력 폴백이from_empty_stored_ladder_host를 새 인자 없이 호출9438ba4e9— 폴백은 종전대로truefcdf1e7d5)가 저장 줄이 앞 간격을 증언하는 테두리 문단(이어지는 마지막 조각 뒤 pi=341)도 13.3px 당김a044f5eb9·0276b5ac6— 다음 문단의 저장 첫 줄이 증언하는 소비량(0..앞 간격)을 쓴다. samples 흡수 사례 118곳 중 117곳은 종전 흡수량과 같다issue_7470 visible_successor_uses_the_original_line_origin…stored_after_partial_table_shared_spacing_px커서 맞춤을 1.9px 어긋나게 함(rowbreak-problem-pages 5쪽)issue_4068 only_the_column_top_table_receives_outer_margind2ea85186)이 host 간격 합의 뒤 몫을 버리고 단 맨 위 표의 바깥 위 여백을 흐름에 넣음c14d69ee1— 흐름 = 표 높이 + host 간격 합 + max(0, 끝 점유 − 합의 뒤 몫). 사양서 표 199 175.9(종전과 같음), 2912695 2쪽 표 3 779.6(devel 과 같음)issue_5846 cut_fragment_defers_partial_nested_tail_row(75544 65쪽 표 619 넷째 행 소실)89f600725)과 devel 의 저장 되감김 꼬리말 여유(STORED_REWIND_FIRST_FRAGMENT_PAINT_FOOTER_GUARD_PX4px)가 같은 쪽 바닥 띠를 겹쳐 뺌ecb928662— 예약에서 여유만큼 뺀다. 한/글은 그 쪽에서 본문 끝 5.9px 위에서 첫 조각을 닫는다(devel 렌더·한/글 PDF 괘선 1px 안 일치)2c53026ab(예약을 첫 행 이월 판정에만)·d293089f3(흐름을 devel 식으로)는 원인을 좁히는 중간 단계였고, 각각ecb928662·c14d69ee1이 대체합니다(이력 보존).바닥 예약의 한/글 오라클 확인
메인터너 권고대로 한/글을 직접 실행해 규칙을 확인했습니다. 한/글 2024(13.0.0.564)로 저장·Print 한 합성(빈 host 문단 기준 자리차지 표, 1열 12행, 행마다 세 줄)입니다.
그래서 바닥 예약은 첫 조각 예산 전체(통째 행·행 안 컷)에 둡니다. 합성 입력은 rhwp 가 같은 문턱의 두 사례씩(4쪽, 46~50%)에서 한/글과 한 줄 다르게 끊어 회귀 fixture 기준(전쪽 90% 이상)에 못 미치므로 저장소에 넣지 않았습니다. 두 여백 계열에서 똑같이 생기는 줄 단위 컷 문턱 차이(약 1px)이며 바닥 예약과 무관합니다(미해결).
검증 (code head
ecb928662, base9418ed0c7)--check· unit tier--check통과.--features native-skia, release-test) 11,762건 중 11,758 통과 — 실패 4건은 devel 에서도 같은 이 host 환경 문제다(issue_7966_pdf_close_stroke3건은 Windows 체크아웃 CRLF, fix(checkout): Windows CRLF·심볼릭 링크 체크아웃에서만 실패하던 로컬 검사를 고친다 #8193 ·custom_font_paths_select_style_within_the_first_family_owner는 글꼴 설치).successor_keeps_spacing_when_its_stored_line_says_so: hwpspec 54쪽 문단 341 첫 줄이 저장 줄 위치(본문 위끝 + vpos)에 있다. 반례로 흡수가 맞는 hwpspec 53쪽 문단 337·사양서 55쪽 문단 349 도 저장 줄에 있다. 수정 전 문단 341 assertion 에서 실패, 수정 후 통과.9418ed0c7Native / WASM…-hancom-official.pdf(--compat 2024)samples/hwpspec.hwp·pdf/hwpspec-2024.pdf위 대표 증적은 이 head 로 다시 캡처했습니다(같은 파일 이름). 33쪽 Native raw 99.20%. samples A/B·10k 코퍼스는 이번 base 에서 다시 돌리지 않았습니다.
최신 devel rebase (2026-10-26,
238de6de4— #8228 통합 반영)기준
upstream/devel238de6de4, code headd878c2ad5, 증적 head567ff9d18(이전5c80b5d48). 위9418ed0c7rebase 의 커밋·보정은 그대로 두고 옮겼습니다.stored_empty_host_frame.rs충돌from_stored_ladder를 저장본·재조판 공용from_ladder로 나눔(재조판은 높이 일치 검사 생략)from_ladder의 검사를 저장본 경로에만 둔다origin_closes_float_frame)from_ladder로 이어지지 않음d878c2ad5— 저장본 경로는 받은 값을,from_reflowed_ladder는 앞 항목 상자 끝을 증언하는 호출자가 없어false검증 (code head
d878c2ad5, base238de6de4, macOS)--prepare→ rustfmt--check· Clippy native / wasm32 lib / workspace all-targets 경고 0 · workspace build · manifest--check --base-ref 238de6de4· unit tier--check --base-ref 238de6de4통과.cargo test --locked --no-fail-fast --profile release-test --tests: 11,486 통과 / 0 실패(issue_7470·issue_4068·issue_5846·issue_8149포함).--silhouette-only, 96dpi,RHWP_FONT_PATH=ttfs/opensource:ttfs/redistributable:rhwp-studio/public/fonts, 판정값silhouette_raw_match_percent):238de6de4Native / WASM…-hancom-official.pdf(--compat 2024)samples/hwpspec.hwp·pdf/hwpspec-2024.pdf(--compat 2024)issue8149/captioned-host-2020issue8149/rowfit-2020issue8149/headerrow-2020issue8149/reservefit-2020hwpspec 23·54쪽은 devel 에서도 같은 값으로 90% 미만이며 이 PR 이 바꾼 쪽이 아닙니다(위 「남은 범위」). 대표 PNG(33·43·47쪽)는 이 head 로 다시 캡처했고 직접 판독에서 표 상자·쪽 소속·뒤 제목 위치가 정본과 맞습니다. 표 캡션 번호(정본 「표 42」, rhwp 「표 1」)는 필드 번호 차이로 이번 범위 밖입니다.
🤖 Generated with Claude Code