Skip to content

fix(xlsx): seat top-aligned fixed-row text on Excel's whole-point top seat - #1744

Merged
developer0hye merged 1 commit into
mainfrom
fix/issue-1606-top-aligned-seat
Sep 15, 2026
Merged

developer0hye merged 1 commit into
mainfrom
fix/issue-1606-top-aligned-seat

Conversation

@developer0hye

Copy link
Copy Markdown
Owner

File submission policy

Before attaching or committing files, read the submission policy.
Complete a security review and obtain your organization's internal approval for
public sharing where applicable. You are responsible for lawful disclosure;
the project and maintainers disclaim related liability to the extent permitted
by applicable law.

  • Any submitted sample files or attachments satisfy the submission policy, or none are submitted.

Summary

A top-aligned worksheet cell in a fixed-height row rested its line box on the cell's top inset, so its first baseline sat at the inset plus the face's continuous ascent. Excel seats that line a whole number of sheet points below the row's top boundary: the face's hhea ascent plus line gap at the declared size, offset by a constant and rounded together, evaluated in sheet space and then printed through the fit-to-page scale. On the public #982 workbook the nine-line Segoe UI 14 instruction block therefore matched native by coincidence at 100% (1pt inset + 15.107pt ascent ≈ 16) and sat 0.79pt low on the 0.82-fitted original; every other top-aligned fixed row was off by the rounding remainder of its own face and size.

Key changes:

Related issue

Fixes #1606
Related: #1063, #1496, #1719, #1659

Testing

  • cargo test --locked -p office2pdf --lib -- top_aligned_ fails on main (the helper does not exist; on the compiled control the first baseline sits at the inset plus the continuous ascent) and passes with the change.
  • cargo test --locked --workspace --no-fail-fast — see the run recorded below.
  • cargo +1.97 clippy --locked --workspace --all-targets -- -D warnings and cargo fmt --all -- --check clean.
  • Native GTs: the Excel for Mac 16.112 exports of the unchanged public workbook (2 pages, SHA-256 2aa03cafa6a156d553a2f1e03e0458257319ff6416a188f1132ce7817ecaeae1) and of the issue's unscaled control (fitToPage=false, 3 pages, SHA-256 0935ca38e497a657d2f972fb7ddfc3163cf69e8edfe6d79d230202b5db5d7bfa); before binary is main at d79360d; both conversions with --font-path for the Office cloud-font cache (Segoe UI) and Excel's DFonts bundle.
  • mutool draw -F trace, page 2 of the fitted original: B4's nine baselines were 154.948/172.168/…/292.708pt (native 154.16/171.38/…/291.92, +0.79pt each); they are now 154.040/171.260/…/291.800pt (-0.12pt each, pitch 17.22pt on both sides). Unscaled control: 177.107 → 177.000pt against native 177.000, all nine lines exact on the 21pt pitch. The XLSX: whether a sheet row's East Asian line box keys on the face or the text is unmeasured #1060 Malgun probe's eight top-aligned ht=36 rows now sit 16pt below their tracks with 0.00pt relative deviation from its native export.
  • python3 scripts/compare_layout.py GT.pdf after.pdf --audit --fine-shift 0.5 --noise-floor 0.5 --json (page 2: 34 matched, 0 fine shifts; before the fix 8 fine shifts of +0.79pt and one re-grouped line); python3 scripts/compare_render.py GT.pdf after.pdf --page 2 --dpi 300 --fine-shift 0.5 --artifacts-dir … --cluster-report … --cluster-dispositions … --strict-clusters (34/34 clusters dispositioned, strict PASS); python3 scripts/compare_text_layer.py (the GT carries two more spaces from native's own word fragmentation, as in the XLSX: category-axis stroke overpaints a coincident line series #1604 audit). Page 1 is byte-identical to the before PDF's page 1 in the trace; a 5% fuzz comparison of the before and after page-2 renders with the sidebar block masked out differs on 0 pixels.

Visual impact

  • No rendered PDF change
  • Rendered PDF change or visual evidence added
  • Reason: N/A

Visual audit

  • Issue: XLSX: top-aligned wrapped worksheet text starts above the native baseline #1606
  • Fixture: tests/fixtures/xlsx/issue_1603_gift_budget.xlsx (the unchanged public Gift Budget and Tracker1.xlsx from Gift Budget and Tracker1.xlsx misc issues #982, SHA-256 25f5dc75dab19ea12042979a61842314ddc226e3e45d447e36b2a2a104112613)
  • Page(s): 2
  • Renderer and DPI: pdftoppm, 300 DPI
  • Evidence mode: fix
  • Layout audit report: assets/bugfixes/issue-1606/layout-audit.json
  • Render cluster reports: assets/bugfixes/issue-1606/render-clusters-page-2.json
  • Reference exporter differences: None
  • Fine-detail threshold: 0.5pt
  • Layout audit page count: Pass
  • Layout audit text flow: Pass
  • Layout audit visible fills: Pass
  • Layout audit rectangle geometry: Pass
  • Layout audit large shifts: Pass
  • Layout audit fine shifts: Pass
  • New follow-up issues found in this audit: None
  • Model vision findings: Inspected the full native and output page 2 side by side, the 5% diff page, a GT/output/diff stack of the whole sidebar instruction block, a GT/before/after stack and the before-vs-GT and after-vs-GT diffs of its first two lines, four 4x crops of the largest remaining sidebar clusters (differ / ily a / s with / v yea) and the before-vs-after diff of the block. Before the fix the block's nine lines sit visibly below native in the first-lines stack and their diff is horizontal bands along every glyph's top and bottom, the signature of a vertical shift; after the fix the lines share native's baselines and the remaining diff is thin vertical-stroke edges that grow toward each line's end, the output glyphs sitting a fraction of a stroke to the right of native on the same baseline (XLSX: fitted worksheet text keeps the physical margin origin while its paint layer snaps to Excel's sheet-space origin #1719 for the fitted text origin, XLSX: Excel for Mac advances each sheet glyph a whole point, so long strings drift up to 1.3pt from our fractional advances #1659 for the whole-point advance drift along the string). The before-vs-after diff is red only on the nine sidebar lines; the title, chart, gridlines, bars, markers, legend, table and footer are on the same pixels before and after. No bold, italic or underlined run is lost; the only hairlines are the 0.60pt chart gridlines and axis, present on both sides at the same x extent, y and width, with no dash pattern on either side; the two zero-value marker rings and the title's glyph edges are the renderer-only differences carried over from the XLSX: category-axis stroke overpaints a coincident line series #1604 audit.
  • GT: assets/bugfixes/issue-1606/gt.jpg
  • Before: assets/bugfixes/issue-1606/before.jpg
  • After: assets/bugfixes/issue-1606/after.jpg
  • Native: None
  • Compare: None

Visual comparison

GT Before After
GT Before After

Required inspection

  • Rendered all evidence at 150 DPI or higher
  • Stored progressive JPEG quality 86 assets with metadata stripped
  • Used Codex/Claude vision to inspect the full GT/output pages, diff, and matched crops
  • Inspected matched region crops at full resolution
  • Ran compare_layout.py --audit --fine-shift PT and dispositioned every fine/large text-instance shift, rectangle geometry deviation, painted-text visibility mismatch, and visible-fill occlusion
  • Ran compare_render.py --cluster-report PATH --strict-clusters and dispositioned every material 5% fuzz diff cluster by explicit ID
  • Inventoried hairlines and border dash styles
  • Inventoried font weight, italic, and underline emphasis

Deviation audit

Check Result
Page count/order Matches GT
Element presence Matches GT
Position/size Remaining: #1719, #1659
Rotation/flip No deviation observed
Fill Matches GT
Stroke/border Matches GT
Shape outline geometry No deviation observed
Text content Matches GT
Font family/weight/style Matches GT
Text color Matches GT
Alignment Fixed: the top-aligned instruction block starts on Excel's whole-point seat at both print scales
Line/paragraph spacing Matches GT
Clipping/overflow Matches GT

Checklist

  • Commits include a Signed-off-by line
  • PR scope contains one root cause
  • Remaining converter or harness deviations each reference an open issue

🤖 Generated with Claude Code

… seat

A top-aligned worksheet cell in a fixed-height row rested its line box on
the cell's top inset, so its first baseline sat at the inset plus the face's
continuous ascent. Excel seats that line a whole number of sheet points
below the row's top boundary: the hhea ascent plus line gap at the declared
size, offset by 1.17pt and rounded together, then printed through the fit
scale. The #982 workbook's nine-line Segoe UI 14 block matched native only
by coincidence at 100% and sat 0.79pt low on the 0.82-fitted original.

`sheet_cell_top_baseline_from_track_top_pt` states the seat, measured on
the #1063 Arial probes (13 sizes, track- and border-independent) and
confirmed on Segoe UI 14 (both #982 exports) and Malgun Gothic 14 (the
#1060 probe's native export). `word_cell_line_box` redistributes the box
around that baseline for a single-row top-aligned sheet cell, keeping the
box height, row advance and wrapped-line leading unchanged; the #1496
fitted lift moves into `fitted_sheet_lift_pt`, shared by the centred and
top seats. Evidence for the fitted page 2 is under
assets/bugfixes/issue-1606/.

Fixes #1606
Related: #1063, #1496, #1719, #1659

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yonghye Kwon <developer.0hye@gmail.com>
@developer0hye
developer0hye merged commit bcbe2b4 into main Sep 15, 2026
18 checks passed
@developer0hye
developer0hye deleted the fix/issue-1606-top-aligned-seat branch September 15, 2026 10:48
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.

XLSX: top-aligned wrapped worksheet text starts above the native baseline

1 participant