Skip to content

fix(html): a pdf mark covers the text it marks - #913

Merged
andiwand merged 1 commit into
mainfrom
fix/a-mark-covers-the-text-it-marks
Sep 20, 2026
Merged

andiwand merged 1 commit into
mainfrom
fix/a-mark-covers-the-text-it-marks

Conversation

@andiwand

@andiwand andiwand commented Sep 20, 2026

Copy link
Copy Markdown
Member

Reported from the app: a highlight does not cover the text it marks. It is not a recent regression — runBox has taken the selection layer's own rect since #852, and barPath since #849.

Two faults, one box.

  • A descender falls out of it. The box is the .sr run's client rect: one em of the substituted sf face, placed from ascent_em, which is clamped to [0.5, 1.0]. The glyph layer draws with the embedded font and its ink runs past that em. Measured over the reference pdfs, the worst 5% of runs stick out 2.3px of a 14.7px line.
  • It breaks at every word gap. selectedRuns took .sr only. The word break lives in the .sg/.sw spacer beside it, so a mark over a sentence came out as a bar per word with a hole between them. On Antragsformular_Sozialtopf_wise19.pdf/page1, 85 glyph runs had no box over them at all — every one a space or an unmapped glyph.

The box now takes its horizontal edges from the runs as before, the spacers included; its vertical edges from the glyphs it stands over; and boxes that touch on a line are joined into one, which is also what a pdf quad is.

glyphRects() reads every .g rect once per mark rather than per run, so this is one layout pass, not one per selected run.

Measured

Share of glyph area a mark covers, over the reference pdfs:

page before after
Auf nach Rom-Herbert.pdf/page1 0.897 0.996
Core_v5.1.pdf/page1 0.958 1.000
style-various-1.pdf/page0 0.989 1.000
Antragsformular_Sozialtopf_wise19.pdf/page1 0.830 0.882

The last page keeps 84 uncovered runs. Those are glyphs with no Unicode at all — the list bullets — which carry no selection-layer run, so they are not in a text selection to begin with.

Checked

test/browser/annotation: 67 checks, none failing, four of them new. The fixture gained a third page with both layers as a pdf view writes them: glyphs whose ink runs below the selection layer's box, and a gap spacer at the word break. Against main's script three of the four fail, and the output is the bug itself — two boxes with a hole between them, and a box 19.2pt tall over 26pt of glyphs.

The box came from the selection layer's run alone. That run is one em of a
substituted font, so a descender falls out of it, and the gap spacers were
left out, so a mark over a sentence became a bar per word with a hole at
every break.

The box now takes its vertical edges from the glyphs it stands over, counts
the spacers, and joins what touches on a line into one box, which is also
what a pdf quad is.

Measured over the reference pdfs: the share of glyph area a mark covers goes
from 0.897 to 0.996 on `Auf nach Rom-Herbert.pdf/page1`, and from 0.958 to
1.000 on `Core_v5.1.pdf/page1`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0149gFxhkvKTBQidz6brU8Vt
@andiwand
andiwand merged commit c82e7f3 into main Sep 20, 2026
30 checks passed
@andiwand
andiwand deleted the fix/a-mark-covers-the-text-it-marks branch September 20, 2026 10:35
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