fix(sel): clamp declared fontSize when it disagrees with rendered height (#4244)

The macOS system-dictionary HUD samples the underlying paragraph's
typography via getRangeTextStyleInWebview so AppKit can re-draw the
small label using the same font / size as the page text. The sampler
trusted getComputedStyle().fontSize directly, which works for the
typical EPUB inline box but breaks badly on pdf.js text layers: each
glyph span carries an intrinsic font-size that reflects the document's
unit-em size before transform: scale(...) shrinks it back to page-
coordinate pixels, so the value can be many times larger than the
on-screen glyph. Forwarded as-is to NSFont, that gives AppKit a giant
attributed string and the yellow highlight rectangle behind the HUD
ends up engulfing neighbouring paragraphs while the laid-out text
overflows off-screen.

Cross-check the declared size against range.getBoundingClientRect().
height as a sanity bound. When the declared value exceeds the inline
box height by more than 30 %, fall back to renderedHeight * 0.85
(roughly the cap-height-to-1.2-line-height ratio) so PDF lookups
converge on a sane scale; otherwise keep the declared value untouched
so normal EPUB body text is unaffected.
This commit is contained in:
loveheaven
2026-05-21 00:14:15 +08:00
committed by GitHub
parent 5ac8564e41
commit 3825f355a7
+29 -2
View File
@@ -162,9 +162,36 @@ export const getRangeTextStyleInWebview = (range: Range): RangeTextStyle | null
if (Number.isFinite(sy)) scaleY = sy!;
}
const fontSizePx = parseFloat(style.fontSize) || 0;
// Cross-check the declared font-size against the range's actual
// visual height. In typical EPUB layout the inline box is roughly
// `font-size × line-height` tall (≈1.2× by default), so a declared
// font-size noticeably *larger* than the rendered height means the
// value isn't a real CSS pixel measurement we can hand to NSFont —
// pdf.js's text layer is the canonical offender: each glyph span
// can carry an intrinsic `font-size` that reflects the document's
// unit-em size before `transform: scale(...)` shrinks it back to
// page-coordinate pixels, leaving `getComputedStyle(...).fontSize`
// many times bigger than the on-screen glyph. Without compensation
// the macOS HUD lays out a giant attributed string and the yellow
// highlight rectangle behind it engulfs neighbouring paragraphs.
//
// Fix: when the declared size exceeds the inline box height by
// more than 30 %, treat the inline box height as the source of
// truth and back out a plausible font-size. 0.85 is the typical
// ratio of cap-height-ish font-size to a 1.2 line-height box; it
// matches normal EPUB body text (the common case) within a few
// percent and converges PDF text layers onto something AppKit can
// render at a sensible scale. Below the threshold (the common
// EPUB case) we leave the declared value alone.
const declaredFontSize = (parseFloat(style.fontSize) || 0) * scaleY;
let fontSize = declaredFontSize;
const renderedHeight = range.getBoundingClientRect().height;
if (renderedHeight > 0 && declaredFontSize > renderedHeight * 1.3) {
fontSize = renderedHeight * 0.85;
}
return {
fontSize: fontSizePx * scaleY,
fontSize,
fontFamily: style.fontFamily,
color: style.color,
};