Skip to content

fix(typeset): 빈 공동 앵커 host 자리차지 표의 띠·흐름에 바깥 여백을 거듭 더하지 않는다 (#7687) - #8157

Closed
planet6897 wants to merge 2 commits into
edwardkim:develfrom
planet6897:fix/7687-offset-float-outer-top
Closed

planet6897 wants to merge 2 commits into
edwardkim:develfrom
planet6897:fix/7687-offset-float-outer-top

Conversation

@planet6897

@planet6897 planet6897 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

요약

빈(글 없는) host 문단의 공동 앵커 자리차지 표에서 바깥 여백이 두 번 더해지던 문제를 고친다. 대상은 #7687 형상, 곧 첫 표 오프셋 0과 둘째 표 양수 오프셋이다.

  • 오프셋 0 표: 흐름 끝이 바깥 여백만큼 더 내려갔다.
  • 양수 오프셋 표: 배제 띠 끝이 바깥 여백만큼 더 내려갔다.

그 띠 뒤에 오는 글 host 의 원점이 내려가서, 그 host 의 둘째 표·제목·뒤 본문이 아래로 밀렸다. 한글문서파일형식 20쪽 pi=133 「3.2.12. 참고문헌」에서 +4.3px 이다.

원인 — 생산 → 소비 경로

  1. typeset/table/block/entry.rs 의 통째 배치는 빈 공동 앵커 host(is_textless_coanchored_float_host)에 두 값을 더해 place_table_with_text 에 넘겼다.
    • whole_placement_height = table_total = 표 상자 + host 앞 간격(= 바깥 위 여백)
    • RowBreak 흐름 꼬리 unreserved_flow_tail = after − after_for_fit = 바깥 아래 여백
  2. place_table_with_text 의 visible float 갈래는 [renderer/layout] 빈 host 문단의 다중 자리차지 표: host 줄·빈 문단이 표 사이 틈을 채우고 띠에 걸리는 줄만 띠 아래로 가야 한다 (한글문서파일형식 19쪽 −17.4px) #7687 부터 이 host 도 받는다. 이 갈래는 표 원점에 바깥 위 여백을, 흐름 끝·배제 띠 끝에 바깥 아래 여백을 스스로 더한다. 그래서 여백이 두 번 들어갔다.
  3. 한글문서파일형식 20쪽 pi=130 둘째 표(오프셋 15023HU, 바깥 여백 141HU)의 배제 띠 끝을 보면 다음과 같다.
    • typeset 379.72px
    • 저장 사다리(pi=132 vpos 28197)와 layout 의 배제 영역 375.96px
  4. 뒤 빈 문단 pi=132 가 그 띠로 뛰어서 pi=133 의 typeset 원점이 3.76px 내려갔다.
  5. 조판: planet6897 PR 27건 통합·공통 보정 및 회귀 검증 #8148 통합 뒤 layout 은 글 host 양수 오프셋 표의 위치로 typeset 확정 배치(HostPlacementConstraint)를 쓴다. 그래서 pi=133 의 7×2 표·제목·본문이 +4.3px 로 그려졌다.

수정

빈 공동 앵커 host 의 자리차지 표도 글 host 처럼 표 상자 높이(effective_height)만 넘기고, 흐름 꼬리를 붙이지 않는다(entry.rs 두 곳). 바깥 여백의 소유자는 place_table_with_text 하나다.

검증

직접 좌표 (96dpi px)

위치 한/글 정본 devel 이 PR
한글문서파일형식 20쪽 둘째 표(7×2) 윗변 681.7 686.0 682.3
「3.2.12. 참고문헌」 849.5 853.9 850.2
「Bibliography …」 880.7 885.1 881.4
합성 K1 T4 윗변 492.4 496.7 492.9
합성 K2 T4 − T3 (기대 9000HU = 120.0) 119.9 123.8 120.0
합성 K3 T4 윗변 471.2 475.4 471.6
합성 K4 (앞 빈 host 없음, 대조군) T4 298.4 298.7 298.7 (불변)

Visual Sweep — raw 2px 실루엣 (silhouette_raw_match_percent)

합성 samples/issue7687/titled-host.hwpx 는 이 PR 의 게이트 입력이다.

  • 생성: gen_titled_host_after_band.py, 저장 줄 없음
  • 정본: 한/글 2020 Print pdf/issue7687/titled-host-2020.pdf, 4쪽 = 4쪽
출력 1쪽 2쪽 3쪽 4쪽
Native devel 79.52 76.86 79.34 95.09
Native 이 PR 100.00 97.11 100.00 95.09
fresh WASM devel 75.8 72.9 80.0 ≥90
fresh WASM 이 PR 100.00 96.96 100.00 94.84

기존 합성 empty-host.hwpx(6쪽) 결과다.

  • Native·WASM 전후 모두 100.00%
  • typeset used 만 3.8px 줄고 SVG 는 바이트 단위로 같다.

한글문서파일형식 HWP 는 진단 자료다.

대표 이미지 (head 53278043f)

한글문서파일형식 20쪽 — 수정 전 / 수정 후 review (Native)

20쪽 review Native devel

20쪽 review Native 이 PR

한글문서파일형식 20쪽 overlay (이 PR, Native / fresh WASM)

20쪽 overlay Native

20쪽 overlay WASM

합성 K1 — 수정 전 / 수정 후 review (Native), overlay (이 PR, Native / fresh WASM)

K1 review Native devel

K1 review Native 이 PR

K1 overlay Native

K1 overlay WASM

직접 판독 결과다.

  • 20쪽: 둘째 표 괘선, 「3.2.12.」 제목, 본문이 정본과 겹친다.
  • K1: T4 와 뒤 글줄이 정본과 겹친다.
  • 남은 차이는 글리프 굵기뿐이다.

회귀 검사

tests/cases/issue_7687_empty_host_multi_float_gap.rs::titled_host_after_empty_host_band_keeps_its_float_offsets 를 추가했다.

기대값은 원본 속성(오프셋 9000HU, 바깥 여백 283HU)에서 계산한 관계이고, 정본 PDF 와 0.5px 안에서 맞는 것을 확인했다.

  • K1·K3: T4 윗변 = T2 아랫변 + 아래·위 바깥 여백
  • K2·K4: T4 − T3 = 오프셋
  • 모든 사례: T4 아래 첫 글줄 = T4 아랫변 + 아래 바깥 여백
결과
수정 전 (devel src) FAIL K1 T4 윗변: 실제 496.7, 기대 492.9
수정 후 PASS
기존 synthetic_empty_host_cases_follow_hancom_gap_rule 전후 PASS

영향 범위

samples 전수 1,317 문서에서 dump-pages 출력이 바뀐 문서는 3개다.

  • 한글문서파일형식 HWP 두 사본
  • issue7687/empty-host.hwpx — used 만 바뀌고 SVG 는 같다

쪽수와 이상 지표(overflow·offCanvas·textOverlap·emptyPage)는 전후 같다.

게이트

  • Rust lint 묶음: fmt check, clippy native, clippy wasm32, workspace build, clippy all-targets, suite manifest --check --base-ref 2e087a5b4 를 실행했다. 통과, error 0.
  • rust-unit-test-tiers --check: 통과
  • nextest(release-test): 11,358 / 11,359 통과
    • 실패 1건 issue_6520_svg_layer_contract::subset_font_bytes_are_deterministic_for_multiple_codepoints 는 검증 환경에 설치된 글꼴 때문에 생긴다.
    • base 에서도 같다.

남은 것 / 비범위

🤖 Generated with Claude Code

최신 devel rebase (2026-10-23, #8173 절차)

  • 기준 upstream/devel def947c21 로 충돌 없이 rebase. head 495fc8f86 (이전 53278043f). 위 SHA 고정 URL 은 새 head 로 갱신했다(같은 파일).
  • 로컬 검증(head 495fc8f86, 빌드 전 소스 touch): fmt · Clippy 3종 · workspace build · manifest --check · unit tier 통과, nextest 11,477건 중 11,476 통과(실패 1건 issue_6520_svg_layer_contract::subset_font_bytes_are_deterministic_for_multiple_codepoints 는 이 host 의 시스템 한컴 글꼴 탓으로 base 에서도 같은 assertion 실패, 무관).
  • Visual Sweep(2px 실루엣 raw, --compat 2022, base = devel def947c21 + 무관 커밋, 바이너리·WASM sha256 확인):
입력 쪽수 rhwp/한/글 base Native / WASM 최저 head Native / WASM 최저 0.1%p 넘게 내린 쪽
samples/issue7687/titled-host.hwpx 4/4 76.86 / 72.87 (90% 미만 1–3쪽) 95.09 / 94.84 (90% 미만 없음) 없음

@edwardkim edwardkim added bug Something isn't working layout 문서 레이아웃/페이지네이션: 표·그림 배치, float/anchor, 줄·행 높이, 겹침·클리핑 rendering SVG/Canvas/PDF 등 렌더 출력과 시각 표시: 그리기, 표시 누락, bbox, paint 정합성 table table rendering and edit labels Oct 22, 2026 — with ChatGPT Codex Connector
@edwardkim edwardkim added this to the v1.0.0 milestone Oct 22, 2026 — with ChatGPT Codex Connector
…#7687)

빈 host 에 자리차지 표가 둘(오프셋 0 / 양수)인 문단은 edwardkim#7687 이후 글 있는 host 와 같은
visible float 배치(place_table_with_text)를 탄다. 그 배치는 표 원점에 바깥 위 여백을,
흐름 끝·배제 띠 끝에 바깥 아래 여백을 스스로 더한다. 그런데 통째 배치 호출부는 이 host 에
흐름용 높이(표 + host 앞 간격)와 RowBreak 흐름 꼬리(아래 여백)를 넘겨, 오프셋 0 표의 흐름
끝과 양수 오프셋 표의 배제 띠 끝이 바깥 여백만큼 더 내려갔다.

한글문서파일형식 20쪽 pi=130: 둘째 표 띠 끝 379.72px — 저장 사다리(pi=132 vpos 28197)와
배치는 375.96px. 뒤 빈 문단이 그 띠로 뛰어 pi=133 「3.2.12. 참고문헌」 host 원점이 3.76px
내려갔고, 통합 뒤 배치가 typeset 확정 원점을 쓰면서 그 host 의 둘째 표(7×2)·제목·뒤 본문이
정본보다 +4.3px 아래에 그려졌다. 첫 표(오프셋 0)는 배치 흐름이 놓아 제자리였다.

글 있는 host 처럼 표 상자 높이(effective_height)만 넘기고 흐름 꼬리를 붙이지 않는다.

- 한글문서파일형식 20쪽: 둘째 표 686.0 → 682.3(정본 681.7), 「3.2.12.」 853.9 → 850.2(849.5)
- 합성 K1~K4(gen_titled_host_after_band.py, 한/글 2020 Print): K1~K3 T4·뒤 글줄 +3.8px → 정본 ±0.5px,
  K4(앞 빈 host 없음) 불변
- 회귀: titled_host_after_empty_host_band_keeps_its_float_offsets — 수정 전 FAIL(K1 T4 496.7, 기대 492.9)
@planet6897
planet6897 force-pushed the fix/7687-offset-float-outer-top branch from 5327804 to 495fc8f Compare October 23, 2026 12:13
@jangster77
jangster77 self-requested a review October 24, 2026 07:24
jangster77 added a commit that referenced this pull request Oct 25, 2026
기존 planet6897 PR #8111·#8141·#8152·#8157·#8159·#8163·#8170·#8176·#8185·#8193의 기능 commit 49개와 통합 보정을 반영합니다. 중복 종료된 #8130의 임시 2개 적용은 제거했으며 기능 적용 수에서 제외합니다.

메인터너 보정은 현재 셀 글줄·물리 프레임·선언 행 높이와 선택 span 정렬의 일관성, 공백·출력 경계 및 요청한 회귀 제외를 처리합니다. sample16 계열과 행정업무운영 편람 HWP/HWPX는 사용자 지시로 검토에서 제외했습니다.

검증 code head 673c1d5f7cdcb5573b793ba2d2abaaedd2602134의 전체 nextest는 11,481 PASS/0 FAIL/45 skip이며 필수 로컬 게이트 14개를 통과했습니다. Native/fresh WASM 각35조건·2,041쪽의 최저 raw 91.87422%/91.82753%,90% 미달·고정 devel 대비0.1%p 이상 하락은 없습니다. 실제 Native15/Canvas2D15/지원CanvasKit12와 초기화 JS/WASM 응답을 확인했습니다. Mac WASM은 no-opt 로컬 대체 빌드입니다.

CI 보정은 편람 Gym 과제8건 제거에 따른 원장 기대 건수와 삭제된 case를 참조한 --print 대상 한 줄입니다. 제품 Rust·Studio source는 로컬 검증 head와 같습니다. 최종 PR head b1e93d8, 테스트 merge 41a30c3eeb6e50464b6806beeaa20dbaa23a77a8의 Full CI·CodeQL·Render Diff·Proptest·Adapter·Gym 검사가 모두 통과했습니다. Rust4 shard 합계11,288 PASS/0 FAIL/45 skip입니다.

원 PR별 검토 기록, 대응하는 review/overlay PNG·전쪽 TSV와 오늘할일을 포함합니다. 머지 후 사용자 지정 옵션2로 실제 CI·merge·후속 처리 기록을 보완합니다. 대표 #7445 전체 해결을 주장하지 않습니다.

Refs #8099, #8105, #7687, #8156, #7874, #8169, #8167, #8172, #4599, #7445, #7765, #7881

Co-authored-by: Jaeuk Ryu <35355437+planet6897@users.noreply.github.com>
@jangster77

Copy link
Copy Markdown
Collaborator

기여해 주셔서 감사합니다. 이 PR의 기능을 검토·체리픽 통합하여 PR #8228로 devel에 반영했습니다.

  • 확인한 원 contributor head: 495fc8f86a1500c17aa6cc6ae55a1afcb072e9bc. 원 source branch의 후속 변경 없이 같은 head임을 다시 확인했습니다.
  • 통합 merge: 3e02fe2749866a8453af3eb3901b9c1a674cccaf; 개별 PR 검토 기록은 문서 PR #8231에 확정했습니다.
  • 최종 head b1e93d8de2d4842f725415aee19bd5b98ecfb8b3의 Full CI·CodeQL·Render Diff·Proptest·Adapter·Gym 계약은 모두 성공했습니다.
  • 로컬 runtime source 673c1d5f7cdcb5573b793ba2d2abaaedd2602134에서 전체 nextest11,481 PASS/0 FAIL/45 skip, 별도 CI A/B/C/D 합계11,288 PASS/0 FAIL/45 skip입니다. 로컬과 CI 실행 수를 구분합니다.
  • 공통 전쪽 검증은 Native/fresh WASM 각각35조건·2,041쪽, 최저91.87422%/91.82753%, 90% 미만·누락·동일 조건 base 대비0.1%p 이상 하락0쪽입니다. 해당 원 PR에 대응하는 입력과 직접 판독은 아래 및 개별 기록으로 구분했습니다.

원 기여에서는 빈 공동 앵커 host 표의 바깥 여백을 중복 소비하지 않습니다.

원 기여와 통합 보정

원 기여를 유지했습니다. #8159에 중첩된 동일 패치를 중복 적용하지 않았습니다.

원 기여 기능과 메인터너의 통합 보정 범위는 위 개별 검토 문서의 적용 commit·소비 경로·실제 회귀 결과에 구분했습니다. 다른 PR의 문서나 제외한 sample16·편람을 이 PR의 개선 증거로 대신하지 않았습니다.

해당 원 PR의 전쪽 비교

조건 Native 쪽수·최저 / TSV fresh WASM 쪽수·최저 / TSV
titled-host 4쪽 / 94.04017% 4쪽 / 94.04017%

전체 페이지 90% 미만·누락 0쪽입니다. 같은 조건의 고정 base 대비 0.1%p 이상 하락은 0쪽이며, 쪽수 개선은 본문 소유·순서 보존을 따로 확인했습니다. 실제 입력·PDF SHA-256과 증적 바이트.

이 원 PR에 대응하는 정식 회귀 2건의 실제 PASS는 개별 검토 기록에 기재했습니다.

직접 확인한 증적

직접 판독: T1~T4 표 띠와 K1 제목·본문을 대조했습니다. 표/뒤 문단의 중복 여백은 보이지 않습니다. 테두리 안티앨리어싱 차이는 남습니다.

#8157 titled-host p1 Native review
#8157 titled-host p1 Native overlay
#8157 titled-host p1 fresh WASM review
#8157 titled-host p1 fresh WASM overlay

Visual Sweep 비교·병합 판정 방법과 공통 producer·해시·잔여 원장을 연결합니다. 일치율은 내용 픽셀 중심 자동 비교 보조값이며 사람 판독의 정확도나 완전한 픽셀 동일성을 뜻하지 않습니다.

이 원 PR은 별도 직접 merge가 아니라 통합 PR에 반영했으므로 종료하겠습니다. contributor의 원격 브랜치는 유지합니다. 관련 이슈 전체의 완료 여부는 개별 증거 범위로 별도 판단하며 이 PR 종료만으로 일괄 종료하지 않습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working layout 문서 레이아웃/페이지네이션: 표·그림 배치, float/anchor, 줄·행 높이, 겹침·클리핑 rendering SVG/Canvas/PDF 등 렌더 출력과 시각 표시: 그리기, 표시 누락, bbox, paint 정합성 table table rendering and edit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants