Add citation/reference hover preview#5611
Add citation/reference hover preview#5611koppor wants to merge 11 commits intosumatrapdfreader:masterfrom
Conversation
Hovering an internal-document link (citation, figure, footnote, etc.) now shows a small popup that renders the destination region of the target page, so the bibliography entry / figure / footnote is visible without leaving the current page. Inspired by PDFRefPreview. Detection: - IsCitationLink: any internal kindPageElementDest link. - DetectEntryBox: per-glyph text+coords scan from the destination anchor, ending at one of: "[N" at the entry's first-line X, indent change back to that X, an X that matches neither first-line nor continuation X, vertical paragraph break, single-line-entry pattern, or column wrap. - Falls back to a fixed strip when text-based detection finds nothing near destY, and to a taller strip when the detected box is too small (figure / diagram fragment). - Result is rendered via engine->RenderPage and blitted into a yellow popup, letterboxed to fit max bounds. Engine API: - IPageDestination::GetDestPoint2() (default returns GetRect2()). - PageDestGetDestPoint() helper. - PageDestinationMupdf caches resolved (destX, destY) from fz_resolve_link at construction. Plumbing: - New RefHover.cpp/.h. - MainWindow gets a refHover member, destroyed in dtor. - Canvas OnMouseMove schedules / hides the popup; OnTimer renders. Configurable via the EnableCitationHover advanced setting (default true). fixes sumatrapdfreader#128 fixes sumatrapdfreader#4221 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Looks very usefull do you have a compiled exe for me to play with while testing some other files |
Thank you! I was searching for that feature for a long time - but none of the existing apps convinced me. Sioyek kind of looked nice, but it has other issues (e.g., ahrm/sioyek#1350)
Sure! I put it at https://builds.jabref.org/main/SumatraPDF-dll.exe . |
Initial render uses page zoom so popup text height matches visible page text. Wheel on the citation link re-renders a region sized to fill the popup at the new zoom (anchored at detection top-left, clamped to page). Wheeling out brings in new page content; wheeling in crops to detail. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Good question — I just added mouse-wheel zoom for the popup. Hover a citation link to bring up the popup, then scroll the wheel while still hovering the link to zoom the preview in/out:
The popup window keeps its initial size; only the rendered content changes. The initial zoom now matches the document's current page zoom, so popup text height is comparable to the visible page text. New build: https://builds.jabref.org/main/SumatraPDF-dll-2.exe |
|
@koppor Fantastic improvement though took me a while to grasp the concept of zoom the source box but once tried out it makes sense as an alternative to try to zoom the quick viewbox (I have had issues with that when attempting a magnifier box as mouse focus is then relocated). The last core issue I see is the "window" is highly varied depending on sources own desired target. Take this silly example, where the target is a wide set of goto with a topbar target. It would be better if the quickview is a fixed ratio. |
Reviewer noted that internal links pointing to non-bibliography destinations (TOC entries, topbar goto targets, page anchors) produced ugly wide-thin popups because the entry-detection fallback returned a strip that ran from destX to the right page margin. Replace those fallback paths with a fixed-pt box anchored on the destination so every non-reference target gets the same popup shape. Bibliography entries that the detector can identify keep their existing (content-shaped) box. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@GitHubRulesOK Good catch — pushed 77b017f to address that. Now the popup shape depends on what the link actually points to:
So your wide-thin "topbar goto" example now gets the same compact box as any other non-reference target, rather than stretching to the right page margin. Build with this fix: https://builds.jabref.org/main/SumatraPDF-dll-3.exe |
|
@koppor I am not trying to be a killjoy this IS a good improvement |
The atIndentX check used a ±5pt tolerance to decide whether a new line was still part of the current bibliography entry. That's too tight when a continuation line switches from roman to italic (or vice versa) — the per-glyph bbox left edge shifts with the new side-bearings, even though both lines actually start at the same pen position. The result was that 3+ line entries with italic continuation (e.g. titles followed by an italicized journal/conference name on a new line) got truncated at the italic line's first glyph, with only the 6pt of padding leaking the top of that line into the popup. Bumping tolerance to ±25pt covers any realistic side-bearing variance while still catching truly external content (footers, author bios) which sit at a meaningfully different X. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Removed the rule that terminated entry detection when a continuation line's first glyph X drifted from the captured indentX. Even with the recently bumped tolerance, font-metric variance on continuation lines (especially when one continuation line starts in italic and another in roman/digit) could still trigger premature termination, cutting off the last line of long bibliography entries. The remaining rules already cover all the realistic next-entry signals: - "[N" at firstLineLeftX (numeric bibs) - new line back at firstLineLeftX after a continuation (hanging-indent) - vertical paragraph break - single-line-entry case If a future PDF has external content following a bibliography entry without any of those signals, we'd over-include — but that's vanishingly rare and clearly preferable to silently truncating real entries. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The thing is that with the AI helper, it is much easier to adress user comments and wishes. I only hope that the code style is good enough. 😅 |
Reviewer noted that the small fixed-ratio popup (360×260pt) used for non-reference targets often shows too little context — e.g. a 'Table N' link surfaces only the caption text, not the table itself; a section- heading link shows only the heading; an image-only PDF link shows a narrow patch of art. Replace it with a landscape view: full page width × half page height, anchored at the destination. Capped by kMaxPopupWidth/Height (the latter already enforced; this commit also wires the width cap into the auto- fit). Also recognise single-line detected entries with no continuation indent as ambiguous (likely caption / heading rather than a real bibliography entry) and route them through the same landscape view. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related fixes:
1. Relax rule (a): a "[" at firstLineLeftX is now treated as a next-entry
marker regardless of what follows. Previously it required a digit
("[1]", "[2]"), which missed alphanumeric description-list styles
like "[Nyg11]", "[Foo+09]", "[KAZ18]".
2. The "single-line + no indent → landscape view" heuristic was firing on
single-line description-list bibliography entries, treating them as
non-references and replacing the fitted entry box with a half-page
landscape slice (which then included the next several entries below).
Skip that redirect when the detected entry starts with "[" — that's a
strong bibliography signal even on a one-line entry.
The destY-at-page-top case (where the citation link's destination has no
specific Y, just page-level) still falls back to the landscape view —
that one needs source-text extraction on the source page to identify the
citation key, which is a larger change.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@kjk can the PR be adjusted to your expectation of common user desires my test with others suggest they use a fixed window size so click figure1 will show a large area around that goto |
Reshape the citation-hover popup so it picks the right region for non-
bibliography destinations:
- "Figure / Table / Listing / Algorithm N.M" caption anywhere below the
detected box: the destination is figure / table / listing body. Show
landscape view so the popup includes the caption. Catches code/console
listings whose lines start with "[TAG]" (linter output, etc.) that
would otherwise short-circuit through the bracket-bib check.
- Code-listing detector via brace/semicolon/paren density: figures whose
body is example code route to landscape view.
- Numbered / labeled headings ("6.2 Foo", "Figure 2.2 ...", "Section 7"):
routed to landscape so the popup includes the heading + content below.
- Tabular indent (continuation X far right of firstLineLeftX): routed to
landscape to show the whole table.
Description-list bibliography improvements:
- Track a descListSibling flag on rule (a) "[" at firstLineLeftX, rule
(b) indent-change-back, rule (c) vertical paragraph break with the
next glyph at firstLineLeftX, and rule (d) single-line case. When set,
the post-loop returns the fitted box even for single-line entries with
no continuation indent — fixes abbreviation-list hovers (JVM, AKM,
ADR, OMT, ...) where blank lines separate sibling entries.
- Recover startIdx via leftmost-on-line walk after the y-band scan: a
poorly-authored link destX can land startIdx mid-line on hanging-
indent layouts, dropping the "[KOS06]" / "Philippe Kruchten" leading
portion from the popup. Walking to the line's actual leftmost glyph
recovers it.
- Trim trailing blank page margin in LandscapeBox so the popup ends just
below the last text glyph instead of including the full page footer.
- Cap popup height by monitor work-area (~90%, max 1400px) and clamp
popup position to the work-area edges so it doesn't get clipped at
the top/left when the cursor is near a screen corner.
Resolve page-level link destinations using the source link's text:
- PageDestGetDestPoint returns {0,0,0,0} when the link has no specific
anchor — common for body-text abbreviation / glossary links. Plumb
the source page + source rect through RefHoverSchedule, then on
destY <= 0 extract the longest alphanumeric run from the source rect
(preferring "(XYZ)"-flanked tokens over bracketed citation keys) and
search the destination page for it. The matching glyph's Y becomes
destY, so DetectEntryBox can crop to the matching entry. Hovering
"(AKM)" body text now yields just the AKM entry, not the whole
abbreviations page.
Re-render guard tightened: the popup short-circuit now also compares
destX/destY, so two adjacent links that share a destination page but
land at different positions (e.g. "Section 7" link + a bib ref both on
page 41) each render their own content rather than reuse a stale popup.
A manual-test checklist comment at the top of RefHover.cpp documents the
case classes covered and the remaining known limits.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Pushed Iterated through several non-bibliography hover targets and edge cases in description-list bibliographies. The detector now handles: Figures / tables / code listings / headings
Description-list bibliographies and abbreviation lists
Page-level link destinations
Popup positioning / re-render
Top-of-file checklist comment in |
|
The new build is available at https://builds.jabref.org/main/SumatraPDF-dll-4.exe |








#128 (comment)
11 years later, Claude can help.
This update is entirely written by claude - "only" driven by me in a code-and-fix mannger
Screenshot:
Summary
Hovering an internal-document link (citation, figure reference, footnote marker, etc.) shows a small popup rendering the destination region of the target page — so a
[1]citation, a "Figure 2.3" reference, or a footnote can be inspected without leaving the current page. Inspired by PDFRefPreview.Fixes #128
Fixes #4221
Demo
Hover any in-text reference for ~300 ms. The popup shows just the relevant entry / figure / footnote, cropped to its actual text box. Works for both 1-column and 2-column layouts, hanging-indent and numeric
[N]bibliographies, and figure references (where the strip falls back to a generous region around the anchor).Implementation
Detection (
src/Canvas.cpp+src/RefHover.cpp):IsCitationLinkaccepts any internalkindPageElementDestlink.DetectEntryBoxdoes a per-glyph text+coords scan starting atdestY, with several boundary signals:[Nat the entry's first-line X, indent change back to that X, an X that matches neither first-line nor continuation X, vertical paragraph break, single-line-entry pattern, and column wrap.destY, and to a taller strip when the detected box is suspiciously small (figure / diagram fragment).engine->RenderPageand blitted into a popup, letterboxed to fit max bounds.Engine API change (would appreciate a quick eyeball on this part):
IPageDestination::GetDestPoint2()insrc/EngineBase.h, default returnsGetRect2()(so existing implementations are unaffected).PageDestGetDestPoint().PageDestinationMupdf(insrc/EngineMupdf.cpp) now storesdestX/destYresolved viafz_resolve_linkat construction; previously the resolved (x, y) fromResolveLinkwas discarded after the page number was extracted.Plumbing:
src/RefHover.cpp/src/RefHover.h.MainWindowgets arefHovermember, destroyed in dtor.Canvas::OnMouseMoveschedules / hides the popup;OnTimerrenders.vs2022/*.vcxprojand*.vcxproj.filtersupdated.Setting:
EnableCitationHoveradvanced setting (defaulttrue, version 3.7), defined incmd/gen-settings.ts. Generated files updated.Test plan
bun ./cmd/build.tsclean — 0 warnings, 0 errors.[N]citations: tight popup of the right column entry only.Bischoff and Küchlin, 2017→ bib entryDaniel Bischoff and Wolfgang Küchlin. Adapting…): single entry, correctly cropped.[N]follows): stops before author bios / footers thanks to the indent-change signal.¹url \n ²url \n …): just the hovered footnote.Figure 2.2): falls back to a generous strip around the anchor so the diagram is visible.EnableCitationHovertofalse: existing popup is hidden, no further popups scheduled.Notes
tests-manual/extract-references/paper1/main.pdffrom the JabRef repo. Not included in this PR.RefHover.cpptext-scan boundary heuristics are defensive (multiple signals), but they're heuristics — happy to iterate based on PDFs that don't crop cleanly.🤖 Generated with Claude Code