A fast, fully native Windows 10/11 desktop viewer that shows two PDF documents side by side (three, in the optional three-pane mode), with synchronized scrolling that actually works: the pairing is a user-adjustable anchor (pre-scroll each pane, lock, done) and positions are exchanged in page units, so documents with different page formats and different zoom levels stay aligned page-for-page.
Pure Win32 + Direct2D, rendered by MuPDF: a single self-contained executable that starts instantly, with no runtime, no redistributable and nothing to install alongside it.
- Two independent panes (three in the optional three-pane mode), each a full PDF viewer: continuous layout, tiled rendering crisp up to 800% zoom, fit-width / fit-page modes, per-monitor DPI awareness.
- Page-by-page mode (Ctrl+4): the view holds one page at a time. When the whole page fits, every key press or wheel notch flips a page; when zoomed in, input scrolls to the page edge first and only a further input flips (backward flips land at the bottom of the previous page, so reading upward stays continuous). Synced panes flip together.
- Synchronized scrolling (F7) with a delta anchor: align any page of one document with any page of the other and the offset is preserved; hold Alt to adjust one pane while keeping the lock. Optional zoom sync (Ctrl+F7).
- Sync points (WinMerge-style, Sync menu): pin whole-page pairs ("page 5 left = page 4 right"). Between points the panes scroll page-for-page; where one document has extra pages the other shows empty alignment gaps, so both panes scroll (and page-flip) together 1:1 (Sync ▸ Show Alignment Gaps, on by default; toggled off, the shorter side waits at the end of its section instead). Sync-point pages are flagged with an anchor mark next to the page and ticks along the right edge (solid = manual, faded = generated). Shift+F7 adds a point at the current alignment and removes it again when the pages in view already carry one - the toolbar button shows which of the two it will do, staying pressed while you are on a point. You can also review and remove points in a dialog, clear them all (Ctrl+Shift+F7), or generate them from bookmarks: numberings present in both documents become sync points ("1.2", "2.2.1", "A.1", or with a verbal prefix in Italian, English, German, French, Hungarian, Ukrainian, Romanian, Portuguese, Greek, Spanish, Polish, Dutch, Czech or Swedish such as "Chapter 3", "Kapitel 3", "Chapitre 3", "Fejezet 3", "Розділ 3", "Capitolul 3", "Capítulo 3", "Κεφάλαιο 3", "Rozdział 3", "Hoofdstuk 3", "Kapitola 3", "Avsnitt 3", "Appendice A"), lettered appendix sub-items ("A ...", "B ...") pair by their letter, and unnumbered bookmarks pair by title at the same outline depth, matching common section names across the same fourteen languages ("Indice" = "Contents" = "Inhaltsverzeichnis" = "Table des matières" = "Tartalomjegyzék" = "Зміст" = "Cuprins" = "Índice" = "Περιεχόμενα" = "Spis treści" = "Inhoudsopgave" = "Obsah" = "Innehåll"), the rest is skipped. Generated points survive auto-reload (re-derived from the fresh outline), and each pair's points are remembered across sessions: reopening the same two documents brings its manual points back and re-derives the generated ones.
- Full-document text search (Ctrl+F) with live highlighting and F3 navigation, plus
Match case, Match whole word and Regular expression toggles in the find bar
(remembered across sessions). Whole-word boundaries are Unicode-aware, so "perché" is a whole
word too, and whole word can be combined with a pattern. Typing never moves the view: the
highlights appear where you are, and F3 steps from the page you are on.
In regex mode the pattern is run when you press Enter, not while you type, so a half-typed
pattern is never executed;
^and$match per line, a match does not cross a line break (write\sfor one), a pattern that does not compile says so instead of reporting no matches, and results are capped at 10000 per document (shown as≥10000). Patterns with nested quantifiers such as(a+)+bcan still take unbounded time on the pane that runs them. - Text selection with the mouse (double-click = word), Ctrl+C copies Unicode text. Copy and the whole find family also live in the Edit menu.
- Clickable links (internal destinations and web URLs) and an outline sidebar (F9).
- Auto-reload: documents rewritten on disk (e.g. by a LaTeX build) reload in place, keeping position, zoom and sync.
- SyncTeX two-way sync with your TeX editor: Ctrl+click a spot in the PDF to jump to the
source line (inverse search), and
-forward-searchjumps the viewer to a source line (see below). - PDF page labels: the status bar, the go-to box and the scrollbar tooltip show the
document's own page labels when they differ from the ordinal, e.g.
ix (9/314)for roman-numbered front matter. - Go to page (Ctrl+G) by number or by label ("ix", "A-3" - labels win on ambiguity), an editable page box in the toolbar row, and a page tooltip while dragging the scrollbar.
- Compact chrome: menu bar and toolbar share ONE row in a rebar (with a chevron overflow when narrow), plus the status bar (page and zoom per pane, with the sync state between the two panes in the two-pane layout and after the rightmost pane's cells in the three-pane one), full screen (F11 / Alt+Enter), a width-adjustable outline sidebar (drag the divider; double-click fits it to the widest bookmark, removing the horizontal scroll), recent files and recent sessions (two or three documents), UI in English, Italian, German, French, Hungarian, Ukrainian, Romanian, Portuguese, Greek, Spanish, Polish, Dutch, Czech and Swedish.
- Pane headers: an optional strip above each pane shows the open PDF's file name (or full path) and doubles as the active-pane cue - the focused pane's header carries an accent underline along the bottom edge, the other stays plain. Toggle the header and the name/path choice in the Options dialog; with the header off, the active pane is marked by a border instead.
- Movable toolbars, Internet Explorer style: untick "Lock the Toolbars" (View menu, or right-click the bar) and grippers appear on the menu, toolbar and page-box bands: drag to reorder or resize them, or wrap them onto extra rows. The right-click menu also carries IE's text options for the command toolbar: "Show text labels" (below the icons, the default), "Selective text on right" (labels beside the primary buttons only) or "No text labels". By default the bars are locked with the menu on its own row and the labeled toolbar plus page box on a second row; "Reset the Toolbar Layout" (next to the lock in both menus) returns to that arrangement. In full screen the bars are locked to one row, so the right-click menu offers only the text options there. The toolbar covers the whole Sync menu too (sync point, from bookmarks, points list, clear, alignment gaps, swap) and the two/three pane choice. The arrangement, the lock state and the text option are remembered.
- Options dialog: reopen the last session or start empty, one window or several, defaults for new documents (scroll mode, zoom, sync locks), sync-point anchor marks and scrollbar ticks on/off, the pane header (off, or showing the file name or the full path; hovering the strip shows the full path when the strip is showing the file name, or a path it had to shorten, and double-clicking it opens another document in that pane), keeping the toolbar and/or the status bar visible in full screen, wheel-scroll lines override, the SyncTeX inverse-search command, and the Explorer context-menu integration. When full screen hides the toolbar, a small floating button in the top-right corner exits full screen.
- Explorer context menu (optional, off by default): "Open left/centre/right in PDF Side
Viewer" on .pdf files, in that order, each with an icon highlighting the pane it targets,
registered per-user under
SystemFileAssociationsso it can NEVER become the default PDF handler. They reuse the running window, and "Open centre" switches it to three panes. Moving the exe requires re-registering (re-tick the Options checkbox, or run-register-shell/-unregister-shellfrom a script). - Swap panes (F8) exchanges the two documents including their view states; with three panes it rotates them instead (left moves to centre, centre to right, right wraps around to left) and Shift+F8 rotates the other way. Sync points survive either way, permuted along with the documents.
- Three-pane mode (optional; the app stays two-pane by default): View ▸ Three Panes (also a pair of toolbar buttons) adds a third document between the other two, and File ▸ Open Centre (Ctrl+Shift+M) switches to it on the spot. Each pane keeps its own splitter, page/zoom cell in the status bar and view state; going back to two panes closes the centre document and remembers it for the next switch. The app stays two-pane unless you ask otherwise: a launch that does not reopen the last session starts from the pane count chosen in Options ▸ Defaults, which is two. Sync points work across all three: "Sync Points from Bookmarks" keeps the headings every open document shares, so a third document can only narrow the map, never distort it. Made for comparing three editions or translations of the same text side by side.
- Drag & drop (drop two or three files at once to fill the panes; three of them turn the
three-pane mode on), double-click an empty pane - or the header strip of a full one - to open
a file there (the Open dialog starts in the folder of the document that pane already holds),
close a document with
Ctrl+W (or the whole session with File ▸ Close Session, which also returns to the default pane
count), command line
(
PdfSideViewer.exe left.pdf right.pdf [centre.pdf], Beyond Compare's argument order, so the THIRD file is the centre one and a third argument turns the mode on), session restore (documents, positions, pane count, window). - Recent Sessions (File menu) remembers the documents that were open together, two or three of them, and reopening one brings its layout back with it. Each remembered session also keeps its manual sync points.
- High contrast: with a high-contrast theme active, the whole interface follows the system palette, including the parts the app draws itself (pane header strips, sync-point anchors and ticks, alignment gaps, text selection and search highlights). The page itself stays as the document defines it; inverting the paper is a separate feature, not planned before 1.0.
- New Window (File menu, Ctrl+N) opens a second viewer, empty and offset from the first one, for comparing two unrelated pairs at once. It keeps your settings but not your documents, and a SyncTeX forward search goes to the window that is already showing that PDF rather than to whichever one happens to be on top. Starting the program again while a viewer is open does the same: the second window comes up empty instead of reopening the documents the first one is showing. Both windows write their settings when they close, and what each of them added to the recent lists and to the sync-point memory is merged rather than overwritten, so closing them in either order keeps both; for the single-value options the last one closed still wins.
- One window instead: tick "Starting the program again reuses the open window" in Options and
a relaunch hands its documents to the window already on screen and exits, the way editors and
browsers do, up to three at once, in the same left/right/centre order the command line takes,
and with no documents at all it simply brings the window forward. New Window leaves the File
menu while that is on (the
-new-windowswitch keeps working for scripts). Off by default: a window here is a whole comparison rather than a document, so handing it a double-clicked PDF replaces work that is on screen.
| Key | Action |
|---|---|
| Ctrl+N | New window (empty) |
| Ctrl+O / Ctrl+Shift+O | Open document in the left / right pane |
| Ctrl+Shift+M | Open document in the centre pane (switches to three panes) |
| Ctrl+W | Close the focused pane's document |
| Tab | Switch pane (cycles through all of them) |
| F7 / Ctrl+F7 | Toggle scroll sync / zoom sync |
| Shift+F7 / Ctrl+Shift+F7 | Add or remove a sync point here / clear sync points |
| Alt + scroll | Adjust one pane while synced (re-anchors; with sync points the tweak is transient) |
| Ctrl+F, F3, Shift+F3 | Find, next match, previous match (Esc closes the find bar) |
| Enter, F3 (regex mode) | The first one runs the pattern, the next one steps through the matches |
| Ctrl+C | Copy selected text |
| F9 | Outline (bookmarks) sidebar |
| Ctrl+wheel, Ctrl +/− | Zoom (anchored at the cursor) |
| Ctrl+0 / Ctrl+1 | 100% zoom |
| Ctrl+2 / Ctrl+3 | Fit width / fit page |
| Ctrl+4 / Ctrl+5 | Continuous / page-by-page scrolling |
| Ctrl+G | Go to page (number or label) |
| F8 / Shift+F8 | Swap the two panes (with three, rotate: left → centre → right; Shift+F8 rotates back) |
| F11 / Alt+Enter | Full screen (Esc exits) |
| Ctrl+click | SyncTeX inverse search (open the .tex source at that spot) |
| Space, Shift+Space, PgUp/PgDn, Home/End, arrows | Navigate |
Compile with SyncTeX enabled (pdflatex -synctex=1, the LaTeX Workshop default). The viewer
reads the .synctex(.gz) next to each PDF.
Inverse search (PDF → editor): Ctrl+click a position in any pane. By default the viewer
opens VS Code at that line through the vscode://file/... protocol. The launch template lives
in settings.ini under [synctex] inverse (%f = file, %l = line; a value containing
:// is treated as a URL, anything else as a command line), e.g. for another editor:
inverse=texstudio --line %l "%f".
Inverse search requires the viewer to run without administrator privileges, and says so instead of launching anything: that template comes from a file the same user's ordinary processes can write, so honouring it from an elevated process would run their command at high integrity. The refusal also applies when the app cannot determine its own elevation, which is the safe answer to an unknown. PDF Side Viewer is a per-user application and never needs elevation.
Forward search (editor → PDF): configure LaTeX Workshop to use PdfSideViewer as the
external viewer in VS Code's settings.json:
PdfSideViewer.exe -forward-search file.tex 123 file.pdf hands the request to the running
instance (the pane holding that PDF scrolls there and flashes the target green; the PDF is
opened if needed) or starts the viewer if none is running. This works per pane: each pane
queries its own .synctex.gz, so a bilingual it/en pair (or trio) gets independent forward
and inverse search.
Requirements: the MSVC C++ toolchain and a Windows 10/11 SDK, from either Visual Studio 2022 or
newer with the Desktop development with C++ workload, or just the Build Tools with the
same C++ workload (no IDE needed). No version is hardcoded anywhere: the scripts ask vswhere
for the newest install and the project asks for that install's own default toolset rather than a
fixed version, so it builds with v143 under VS 2022 and with v145 under the 2026 Build Tools.
x64 and ARM64 are both supported, and every script defaults to the architecture of the
machine it runs on.
MuPDF (official 1.28.x source release, thirdparty/ included) is not tracked in this
repository; a script downloads and unpacks it into vendor/mupdf.
-
Fetch and build MuPDF once per platform:
powershell scripts\get-mupdf.ps1 -Build # this machine's architecture powershell scripts\get-mupdf.ps1 -Build -Platforms x64,ARM64 # both
(equivalent to building
vendor\mupdf\platform\win32\mupdf.sln, targetslibmupdf;libthirdparty;libresources, with the newest toolset installed (the vendored projects still declare v142);libresourcesonly exists in Release and is shared by Debug builds.) -
Build the app:
powershell scripts\build.ps1 -Configuration Release # host architecture powershell scripts\build.ps1 -Configuration Release -Platform ARM64
(equivalent to
msbuild PdfSideViewer.sln -p:Configuration=Release -p:Platform=ARM64 -m; the VS Code build tasks call this script, so they work on any machine.)
The result is build\<platform>\Release\PdfSideViewer.exe, statically linked against MuPDF and
against the C runtime (/MT), so it depends on nothing but the operating system. The MuPDF
libraries are rebuilt with the same static CRT by get-mupdf.ps1, since the two flavours cannot
be mixed. scripts\make-release.ps1 packages it into a
portable zip; scripts\make-installer.ps1 (requires
Inno Setup 6.3+, e.g.
winget install JRSoftware.InnoSetup) builds PdfSideViewer-Setup-x64.exe and
PdfSideViewer-Setup-arm64.exe, per-user installers (no admin, silent-install capable) with
optional Explorer-menu integration. The two share an upgrade identity, so the native ARM64 setup
replaces an x64 install that was running under emulation.
Design notes and architecture are documented in docs/DESIGN.md.
PdfSideViewer is free software, released under the GNU General Public License v3 (see LICENSE). It statically links MuPDF, Copyright © Artifex Software, Inc., licensed under the GNU Affero General Public License v3; per GPLv3 §13 the combined work is distributed with both licenses satisfied and full corresponding source available in this repository.