Fix array rule corner overhang: outer vertical rules flush with horizontal rules#261
Fix array rule corner overhang: outer vertical rules flush with horizontal rules#261kostub wants to merge 4 commits into
Conversation
Horizontal rules span the full content box [0, contentWidth], but columnOffsetsForTable: added padding on *both* sides of every vertical rule — including outside the outermost rules. That inset the outer verticals by `padding` from the box edges, so every \hline overhung the outer `|` by `padding` on each side (visible as horizontal lines poking past the box corners; MathJax/LaTeX meet flush). Drop the outside padding on the outer boundaries (i==0, i==numColumns): padding now lives only between a rule and the cell content, so the outermost rules sit flush at x==0 / x==contentWidth — the same edges the horizontal rules span. Interior boundaries keep padding on both sides. Matrix path is untouched (fires only when verticalLines[i] > 0). testArrayRuleGeometryIsDeterministic now pins the flush-corner invariant: the leftmost v-rule stroke edge is at x=0 and each h-rule's ends coincide with the outer v-rules' stroke edges instead of overhanging. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the table layout logic in MTTypesetter.m to remove padding on the outer sides of the outermost vertical rules, allowing them to sit flush with the box edges and align perfectly with horizontal rules. Unit tests in MTTypesetterTest.m have been updated to assert this correct geometry. There are no review comments, so I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
A stroked path straddles its centre-line by thickness/2 on each side, but
initWithStart: recorded the box with the thickness extending to only one
side of the line (position = the centre-line point, width/ascent = full
thickness). So the box sat half a thickness off the actual stroke on the
thickness axis. recomputeDimensions folds that in via max(position.x+width)
and max(position.y+ascent), so:
- the rightmost vertical rule reported right edge = centre + thickness,
over-reporting table.width by thickness/2 — and width drives advance,
so every ruled array injected thickness/2 of trailing blank space;
- the top/bottom \hline over-reported ascent/descent by thickness/2,
inflating \left...\right delimiter height and vertical advance.
Record position as the box's lower-left origin (inset thickness/2 on the
thickness axis) so the bounds exactly cover the stroke; -draw: re-derives
the centre-line by offsetting thickness/2, so the drawn output is
byte-identical — only the reported metrics tighten.
testArrayRuleGeometryIsDeterministic now also asserts table.width equals
the rightmost rule's true right edge (no phantom half-thickness).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code Review — array rule corner fixesVerdict: Ready to merge ✅ (2 non-blocking minor comment fixes recommended) Review verified the two central claims by algebra and by building/running the full suite in an isolated worktree — not taken from the commit messages at face value. Strengths (verified)
IssuesCritical: none. Minor:
Notes
AssessmentReady to merge: Yes — with the two trivial comment fixes recommended but non-blocking. Both geometry fixes are mathematically correct and verified; the draw round-trip is provably byte-identical, the tightened bounds exactly cover the stroke, the matrix path is genuinely untouched, and the full 407-test suite passes. 🤖 Review dispatched via superpowers:requesting-code-review |
The header comment still said callers must offset `start` by thickness/2 for edge alignment. Since "Tighten MTRuleDisplay bounds to the drawn stroke", the initializer performs that inset itself and both callers pass the raw centre-line — following the old comment would double-inset by thickness/2. Rewrite it to state `start` is a centre-line point and the initializer records the box origin. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # iosMath/render/internal/MTMathListDisplayInternal.h
Summary
Two related fixes to array rule rendering, exposed while wiring up the array-environment examples.
Bug 1 — corner overhang
\begin{array}{|c|c|} \hline a & b \\ \hline c & d \\ \hline \end{array}rendered with the horizontal rules overhanging the outer vertical rules — the\hlines poked past the box corners instead of meeting them. MathJax/LaTeX draw a clean box.Root cause.
columnOffsetsForTable:added2·paddingaround every vertical rule, including outside the outermost ones. Nothing sits outside the outer rules, so that padding just inset them from the box edges the horizontal rules span — instrumenting the example (padding = 4.0,thickness = 0.8) showed v-rules confined to[4.0, 63.38]while h-rules spanned the full content box[0, 67.38].Fix. Drop the outside padding on the outer boundaries (
i == 0,i == numColumns); padding now lives only between a rule and cell content. The outermost rules sit flush atx == 0/x == contentWidth— the same edges the horizontals span — so corners meet. Interior boundaries keep padding on both sides. The matrix / existing-env path is untouched: the newpadLeft/padRightlogic only fires whenverticalLines[i] > 0, which is empty for every non-array table.Bug 2 — rule bounds off by half a thickness
A stroked
MTRuleDisplaypath straddles its centre-line bythickness/2, but the display recordedpositionon that centre-line while reportingwidth == thickness(vertical) /ascent == thickness(horizontal). SorecomputeDimensions(max(position + extent)) counted the box as reachingcentre + thickness, over-reporting the parenttable.widthbythickness/2on the right.This is not cosmetic: layout advances the pen by a child's reported width, so every ruled array pushed following content
thickness/2too far right (and surrounding delimitersthickness/2too tall).Fix. Record
positionas the box's lower-left origin (inset bythickness/2on the thickness axis) and re-derive the stroke centre-line in-draw:. Drawn pixels are byte-identical; only the reported metrics tighten to exactly cover the stroke.Tests
testArrayRuleGeometryIsDeterministic— pins the flush-corner invariant: leftmost v-rule stroke edge atx=0, and each h-rule's ends coincide with the outer v-rules' stroke edges rather than overhanging. Fails against the old code, passes after Bug 1.testRuleDisplayHorizontalAndVerticalMetrics— updated to the straddling-box semantics (positionis the box origin, offset bythickness/2from the centre-line).🤖 Generated with Claude Code