Typeset an eighteen-page article and the PDF comes out at 76,974 bytes; the .synctex.gz that lands beside it is 159,347 — more than twice the size of the thing it describes. That bloated map is SyncTeX, and it does exactly one job: remember which line of your LaTeX source became which rectangle on which page. This page opens the file up, runs both directions by hand with synctex view and synctex edit, explains why a click lands on a line rather than on the word you aimed at, and ends with the checklist for when forward search does nothing at all.
What -synctex=1 actually produces
Pass -synctex=1 and the engine writes one extra file next to the PDF, with the same base name: main.synctex.gz. Leave it off and nothing is written at all — which is the single most common omission in a SyncTeX setup. The value is not a boolean but a set of bits, spelled out in man synctex: 0 or absent means no file, a positive value means gzip, a negative value means plain uncompressed text, the 2 bit keeps the compression but drops the .gz from the name, 4 turns on form support for pdfTeX, and 8 compresses harder. Everything at once is -synctex=15. LuaTeX alone insists on the two-dash form, --synctex=1. The machinery ships the same way in TeX Live and in MiKTeX, and pdfLaTeX, XeLaTeX and LuaLaTeX all produce the same kind of map.
pdflatex -synctex=1 main.tex # writes main.synctex.gz
xelatex -synctex=1 main.tex
lualatex --synctex=1 main.tex # LuaTeX wants two dashes
pdflatex -synctex=-1 main.tex # writes main.synctex, plain text
pdflatex -synctex=2 main.tex # writes main.synctex -- still gzip inside!That 2 bit hides a small trap. The file -synctex=2 produces is named main.synctex, but file reports gzip data inside it. Trust the extension, open it in less, and you get binary noise and the impression that SyncTeX wrote a corrupt file. If you only want to read it, use -synctex=-1 instead. Where the command line is out of reach — a GUI that builds with one button — the TeX primitive \synctex=1 at the top of the source turns it on as well. But that route only ever gives you the compressed form: writing \synctex=-1 still produced main.synctex.gz on TeX Live 2024 here. For plain text, the command line is the only door.
| Value | File written | Contents |
|---|---|---|
(none) | — | nothing is written; neither direction works |
-synctex=0 | — | same as absent; the explicit way to switch it off |
-synctex=1 | main.synctex.gz | gzip-compressed; the everyday choice |
-synctex=-1 | main.synctex | plain text; the debugging form |
-synctex=2 | main.synctex | named as if plain, but gzip inside — confusing |
-synctex=15 | main.synctex | bits 1+2+4+8: form support and stronger compression too |
Unzipping the .synctex.gz and reading what is inside
The contents are line-oriented text; pipe it through gunzip -c main.synctex.gz and you can read it directly. There are four sections: preamble, content, postamble, and post scriptum. The preamble holds the version and the Input: table, which numbers every file TeX opened, starting at 1. Not only your main.tex but article.cls, size10.clo, every .sty, and main.aux all get a tag — which is half the reason the map grows so fat. Then Magnification, Unit, X Offset and Y Offset define the coordinate system: Unit:1 means every number below is in sp (scaled points, one 65536th of a point), and X Offset:4736287 is exactly one inch, the margin TeX has always taken from the top-left corner of the paper.
$ gunzip -c main.synctex.gz # abridged; ... marks omitted lines
SyncTeX Version:1
Input:1:/home/you/doc/./main.tex
Input:2:/usr/local/texlive/2024/texmf-dist/tex/latex/base/article.cls
Input:3:/usr/local/texlive/2024/texmf-dist/tex/latex/base/size10.clo
...
Input:10:/home/you/doc/./chap.tex
Output:pdf
Magnification:1000
Unit:1
X Offset:4736287
Y Offset:4736287
Content:
!962
{1
[1,17:4736286,46220574:26673152,41484288,0
(1,4:8799518,8865054:22609920,655359,0
x1,4:9330359,8865054
k1,4:31409438,8865054:11586343
...
)
]
}1The content section is a record of nested boxes. {1 … }1 is one sheet, that is one page; square brackets [ … ] are a vertical box and parentheses ( … ) a horizontal one. Each opener has the shape tag,line:x,y:width,height,depth, so (1,4:8799518,8865054:22609920,655359,0 says “a horizontal box born on line 4 of tag 1, which is main.tex.” In the example above, line 4 of main.tex was \section{Forward and inverse}. The first character of a line names the record type: x is the current point, k a kern, g glue, $ math, f a pdfTeX form reference, v and h empty vertical and horizontal boxes, and ! a byte offset so a reader can seek into the middle of the file.
Recording every page at that granularity makes the file big. For the eighteen-page article from the opening, the compressed map was 159,347 bytes and the decompressed one 638,962 bytes — over eight times the PDF, spread across 24,717 lines. That is why .synctex.gz is a regenerable working file rather than a deliverable: put it in .gitignore, and add it to latexmk’s @generated_exts so cleanup sweeps it away. The synctex(5) man page is blunt on a related point: the structure should not be considered public, and nothing other than the synctex command and the synctex_parser library should need to parse it. Reading it to understand a problem is fine; building a tool that depends on it permanently is not.
# .latexmkrc -- keep -synctex=1 on every build, and clean the map up afterwards
$pdf_mode = 1;
$pdflatex = 'pdflatex -synctex=1 -interaction=nonstopmode -file-line-error %O %S';
@generated_exts = (@generated_exts, 'synctex.gz');Running forward search and inverse search by hand
Forward search (source → PDF) is synctex view; inverse search (PDF → source) is synctex edit. What your editor and viewer call behind their buttons is either these two or an equivalent, so when forward search in LaTeX misbehaves, running them directly separates the two possible culprits at once: a bad map, or a bad editor-viewer handshake. Forward search takes -i line:column:file and -o pdf, and answers with a page number and a rectangle.
$ synctex view -i 5:1:main.tex -o main.pdf
This is SyncTeX command line utility, version 1.5
SyncTeX result begin
Output:main.pdf
Page:1
x:153.195526
y:156.585541
h:133.768356
v:158.522720
W:343.711060
H:8.855677
before:
offset:-1
middle:
after:
SyncTeX result endThe x and y pair is the point to show; h, v, W, H are the left edge, baseline, width and height of the rectangle to highlight. The units are PDF points, so v:158.52 means 158.52pt down from the top of the page. Your viewer takes those numbers, scrolls, and flashes a W by H band. The other direction simply throws a coordinate back.
$ synctex edit -o 1:153.19:156.58:main.pdf
This is SyncTeX command line utility, version 1.5
SyncTeX result begin
Output:main.pdf
Input:/home/you/doc/./main.tex
Line:5
Column:-1
Offset:0
Context:
SyncTeX result endThe argument is -o page:x:y:pdf, and what comes back is an absolute file path and a line number. Your viewer takes that Input: and Line: and substitutes them into the command that launches the editor. The eye-catching part is Column:-1. The format can represent a column, but the engines do not write one, so inverse search is in practice always line-granular. That is why your editor drops the cursor at the start of the line — it is not a misconfiguration.
Why the jump lands on a line, not on the word you clicked
Because the unit of correspondence is a typeset box. TeX turns a paragraph into one long horizontal list and only cuts it into lines at the very end. What SyncTeX remembers is the resulting boxes and the source line that produced each — not words, not characters. Measure it and the asymmetry is obvious. Take a source in which twelve short words sit on twelve consecutive lines with no blank line between them: they collapse into just two line boxes. Ask forward search about source lines 5 through 12 in turn and every one of them answers with the same coordinate.
# twelve short source lines 5..16, no blank line between them
$ for l in 5 6 7 8 9 10 11 12 13 14 15 16; do
> printf "src %-3s " $l; synctex view -i $l:1:res.tex -o res.pdf | grep "^v:"
> done
src 5 v:230.405960
src 6 v:230.405960
src 7 v:230.405960
src 8 v:230.405960
src 9 v:230.405960
src 10 v:230.405960
src 11 v:230.405960
src 12 v:230.405960
src 13 v:242.361130
src 14 v:242.361130
src 15 v:242.361130
src 16 v:242.361130The interesting part is that the reverse direction is a little smarter. Walk synctex edit from left to right across that one line box and it returns different source lines depending on the horizontal position — and often more than one candidate per point, of which the viewer normally takes the first. So forward search is coarse and inverse search is fine-grained. Turn it around: in a paragraph where a single long source line wrapped into eight typeset lines, clicking any of the eight returned line 3 every time, because there was only ever one source line to remember. Landing a word off target inside a TikZ picture, inside the expansion of an intricate macro, or inside a table is the same box-granularity story, not a bug.
# same typeset line (baseline v=230.4), scanning left to right
$ for x in 135 185 235 310 360 435 460; do
> printf "x=%-4s " $x; synctex edit -o 1:$x:229:res.pdf | grep "^Line:" | tr "\n" " "; echo
> done
x=135 Line:5
x=185 Line:5
x=235 Line:6 Line:7
x=310 Line:7 Line:8
x=360 Line:9 Line:10
x=435 Line:10 Line:11
x=460 Line:11 Line:12There is a practical corollary. Write your source as one enormous line and SyncTeX’s resolution collapses to a single point for that whole paragraph. Break at sentences, or at least at clause boundaries, and inverse search starts hitting what you aimed at. The way of writing that makes version-control diffs readable and the way that makes SyncTeX precise happen to be the same way.
Why the line numbers drift once you use \input files
The short answer is that \input is not itself the cause of any drift. Every record carries a tag as well as a line number, and the tag indexes the Input: table. A child file gets its own tag, and its line numbers are line numbers within that child. Measured: clicking inside a chapter pulled in with \input{chap} returned chap.tex as the Input: and the line number within that file as the Line:. Chain twenty chapters together and nothing gets added up.
There are two real causes. The first is a stale map. A .synctex.gz is a photograph of one compile, so if you add three lines to the top of chap.tex and run inverse search without rebuilding, the map still answers Line:3 even though the text has moved to line 6. If the offset is exactly the number of lines you inserted, this is almost certainly it. The second is the absolute path. What goes into Input: is the full path as it was at compile time, so moving the project, opening it through a symlink, or compiling inside a container and viewing outside it all point the viewer at a path that no longer exists. When the wrong file opens — or nothing opens — rather than the wrong line, suspect this one.
The inverse-search command string, viewer by viewer
Inverse search is configured on the viewer side. You hand the viewer a template that says: when someone clicks, fill in this line number and this file name, then run this command. The nuisance is that the placeholder syntax differs from viewer to viewer. zathura uses braces — %{line} and %{input}; Skim uses %line and %file; SumatraPDF and Okular use %l and %f. Most settings copied from elsewhere fail for exactly this reason: the command is right and only the placeholders do not match.
| Viewer | Platform | Line and file placeholders |
|---|---|---|
zathura | Linux / BSD | %{line} and %{input}, via set synctex-editor-command |
Skim | macOS | %line and %file, under Preferences ▸ Sync ▸ Preset: Custom |
SumatraPDF | Windows | %l and %f, in the inverse-search box under Settings ▸ Options |
Okular | Linux / Windows | %l and %f, set under Settings ▸ Editor (for Kile, kile --line %l) |
Adobe Acrobat / Reader | all | no SyncTeX support; inverse search is simply not available |
# zathura (~/.config/zathura/zathurarc)
set synctex true
set synctex-editor-command "nvim --headless -c 'VimtexInverseSearch %{line} %{input}'"
# Skim -- Preferences > Sync > Preset: Custom
Command: nvim
Arguments: --headless -c "VimtexInverseSearch %line '%file'"
# SumatraPDF -- Settings > Options > inverse search command-line
cmd /c start /min "" nvim --headless -c "VimtexInverseSearch %l '%f'"
# Okular -- Settings > Configure Okular > Editor
kile --line %lTriggering forward search from the editor side is straightforward: in TeXShop with Skim it is Cmd-click in the PDF, and Shift-Cmd-click for the other direction. TeXstudio uses Ctrl-click, or the “Go to PDF” and “Jump to source” menu items. VS Code with LaTeX Workshop uses Ctrl/Cmd+Alt+J. One macOS-specific trap deserves naming here: the /usr/bin/vim that ships with macOS is built -clientserver, so there is no channel for anything outside to call the editor back, and the usual inverse-search snippet silently does nothing. The fix is MacVim, Homebrew’s Vim, or Neovim.
What happens on the DVI route (pLaTeX / upLaTeX → dvipdfmx)
The headline is that on the default settings you need to do nothing, and the coordinates match the direct-to-PDF route. You pass -synctex=1 to the engine (platex or uplatex), not to the converter. The engine writes the .synctex.gz alongside the DVI, and its preamble says Output:dvi rather than Output:pdf. Running dvipdfmx afterwards does not touch that map at all — comparing the file before and after with cmp here showed it byte-for-byte identical, and dvipdfmx has no -synctex option in the first place. Incidentally, the dvipdfmx in TeX Live 2024 is a symlink to xdvipdfmx: one and the same binary as the converter used for XeTeX.
# the same source, both routes, same forward-search question
$ pdflatex -synctex=1 cmp.tex && synctex view -i 3:1:cmp.tex -o cmp.pdf
h:133.768356 v:137.554138
$ latex -synctex=1 cmp.tex && dvipdfmx cmp.dvi
$ synctex view -i 3:1:cmp.tex -o cmp.pdf
h:133.768372 v:137.554153
# the two agree to about 2e-5 pt -- nothing needs reconcilingSo what is synctex update for? Exactly what its manual says — to update the SyncTeX file once a dvi/xdv to pdf filter has been applied — and it is needed only when that conversion was given a magnification or an offset. You pass -m, -x and -y the same values you passed the filter. The implementation is the interesting part: synctex update does not rewrite the map. Running it with -x 20mm and comparing the file byte-for-byte before and after showed that it merely appends a gzip blob after the trailing Post scriptum: line. Decompress that blob and it holds one line: X Offset:20mm. In other words, the fourth section of the format is a place for a downstream converter to stick a correction to the coordinate system on like a sticky note. In day-to-day work ptex2pdf or latexmk runs this chain for you and the subject never comes up.
When SyncTeX does nothing: what to check, in order
Start by checking that a .synctex.gz sits in the same folder as the PDF. If it does not, the build is missing -synctex=1. The easy thing to overlook here is the build command your editor ships with. Kile’s bundled PDFLaTeX tool, for instance, does not include -synctex=1 in its default options, and that omission is the single biggest cause of “I configured it and nothing synchronises.” Ticking a SyncTeX box in an editor’s preferences does not always change the command that actually runs.
- Is there a map? Check for the
.synctex.gzwithls. If it is missing, add-synctex=1to the build command — and treat your editor’s default settings as suspect. - Have the PDF and the map been separated? Using
-output-directoryis fine, because both land in the output folder together, but copying the PDF out on its own leaves the map behind and nothing happens. Measured here: after copying onlymain.pdfout ofbuild/,synctex viewreturned in silence. - Is the map stale? Did you rebuild after saving? If the offset equals the number of lines you just inserted, that settles it. Running latexmk with
-pvc, so that every save recompiles, removes this failure almost entirely. - Did you typeset the document you are looking at? Compiling a chapter file on its own gives a map that describes that chapter’s PDF, not the book’s. Check that your editor’s master-file or root-document setting points where you think it does.
- Does the viewer support SyncTeX? Adobe Acrobat / Reader cannot do inverse search at all. Switch to Skim (macOS), SumatraPDF (Windows), or Okular and zathura (Linux).
- Are the placeholders right? Mixing up
%{line},%lineand%lis hard to spot precisely because the rest of the command is correct. - Split the problem at the command line. Run
synctex viewandsynctex editdirectly. If they answer correctly, the map is healthy and the fault is in the editor-viewer handshake. Note thatsynctexexits 0 even when it finds nothing, so a script has to inspect the output rather than the status.
Finally, the loop that turns SyncTeX from a setting into a proofreading habit. Read the PDF, click a word that bothers you, land in the source, fix it, save, rebuild, and use forward search to get back to the spot you just corrected. When that loop runs smoothly, the time you spend hunting through a long source for the place to edit drops to zero. The name Jérôme Laurens chose for what he built — Synchronize TeXnology — sounds grand, but what it actually buys you is that one thing: never having to look.