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:
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user