LaTeX Forward PDF Search with Emacs
I wrote a blog post on implementing inverse PDF search with okular and emacs here. This post is about the reverse: forward search. That is, with point at any position in the LaTeX source while editing in emacs, a keystroke causes okular to center the corresponding portion of the PDF in its viewable area. A nice overview of LaTeX synchronization can be found here.
In the comments on the inverse search post, B. Slade suggested the procedure at http://www.bleedingmind.com/index.php/2010/06/17/synctex-on-linux-and-mac-os-x-with-emacs for forward search. However, the instructions there require installation of AuCTeX version 11.86, while the latest version in the Ubuntu 10.04 repositories have is 11.85.
I managed to kludge a solution for the AuCTeX shipping with Ubuntu 10.04 by slightly modifying some emacs code I found here: http://firstname.lastname@example.org/msg04913.html. Most of the work was already done by Mark Altern and earlier authors; the only change was to tell it to use the .pdf instead of the .dvi.
A note of caution. Forward search with okular is not very convenient. This is because okular redisplays the PDF document every time you do a forward search — with a side “contents” pane and also repositioning the PDF. So if you’ve removed the space-hogging contents pane (by pressing F7 twice) and zoomed and positioned the PDF to your liking, doing a forward search will undo all of that. You’ll have to remove the contents pane again and re-zoom and re-position. This severely limits the usefulness of forward search. There doesn’t seem to be a way around this for now.
Anyway, here are the steps.
- Follow instructions here to set up inverse search.
- Source for okular-search.el is at the end of this post. Copy it and put it in a file called okular-search.el.
- In your .emacs file, add the following code:
(add-to-list 'load-path "/path/to/okular-search.el") (require 'okular-search) (add-hook 'LaTeX-mode-hook (lambda () (local-set-key "\C-x\C-j" 'okular-jump-to-line))) (add-hook 'tex-mode-hook (lambda () (local-set-key "\C-x\C-j" 'okular-jump-to-line)))
That’s it. Press C-x C-j to open a new okular viewing window. Subsequent presses of C-x C-j will reposition the PDF in that okular window to correspond to whatever’s at point in emacs.
Here’s the code for okular-search.el:
;;; (X)Emacs frontend to forward search with kdvi. See the section on ;;; FORWARD SEARCH in the kdvi manual for more information on forward ;;; search, and for an explanation how to use this script. This script ;;; is a modified version of the script "xdvi-search.el" by Stefan ;;; Ulrich, version 2000/03/13. The ;;; modifications were performed by Stefan Kebekus ;;; . Tested with Emacs 20.7.1 and Xemacs 21.4. ;;; ;;; This program is free software; you can redistribute it and/or ;;; modify it under the terms of the GNU General Public License as ;;; published by the Free Software Foundation; either version 2 of the ;;; License, or (at your option) any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with this program; if not, write to the Free Software ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ;;; 02110-1301, USA. ;;; ;;; Please report bugs or improvements, etc. via the "Report bug"-Menu ;;; of okular. ;;; (defvar okular-script "okular" "*Name of start script for okular.") (defun okular-jump-to-line () "Call okular-script to perform a `forward search' for current file and line number. See contents of okular-script for details. If AucTeX is used, the value of TeX-master-file is used as filename for the master .dvi file; else, the return value of okular-master-file-name is used (which see)." (interactive) (save-excursion (save-restriction (widen) (beginning-of-line 1) (let* (;;; current line in file, as found in the documentation ;;; of emacs. Slightly non-intuitive. (current-line (format "%d" (+ 1 (count-lines (point-min) (point))))) ;;; name of the `main' .tex file, which is also used as .dvi basename: (master-file (expand-file-name (if (fboundp 'TeX-master-file) (TeX-master-file t) (okular-get-masterfile (okular-master-file-name))))) ;;; .dvi file name: (pdf-file (concat (file-name-sans-extension master-file) ".pdf")) ;;; current source file name. (filename (expand-file-name (buffer-file-name)))) (start-process "okular" "okular-output" "okular" ;;; src-args ;;; args for -sourceposition: "--unique" (concat "file:" pdf-file "#src:" current-line filename) ))))) (defun okular-get-masterfile (file) "Small helper function for AucTeX compatibility. Converts the special value t that TeX-master might be set to into a real file name." (if (eq file t) (buffer-file-name) file)) (defun okular-master-file-name () "Emulate AucTeX's TeX-master-file function. Partly copied from tex.el's TeX-master-file and TeX-add-local-master." (if (boundp 'TeX-master) TeX-master (let ((master-file (read-file-name "Master file (default this file): "))) (if (y-or-n-p "Save info as local variable? ") (progn (goto-char (point-max)) (if (re-search-backward "^\\([^\n]+\\)Local Variables:" nil t) (let* ((prefix (if (match-beginning 1) (buffer-substring (match-beginning 1) (match-end 1)) "")) (start (point))) (re-search-forward (regexp-quote (concat prefix "End:")) nil t) (if (re-search-backward (regexp-quote (concat prefix "TeX-master")) start t) ;;; if TeX-master line exists already, replace it (progn (beginning-of-line 1) (kill-line 1)) (beginning-of-line 1)) (insert prefix "TeX-master: " (prin1-to-string master-file) "\n")) (insert "\n%%% Local Variables: " ;;; mode is of little use without AucTeX ... ;;; "\n%%% mode: " (substring (symbol-name major-mode) 0 -5) "\n%%% TeX-master: " (prin1-to-string master-file) "\n%%% End: \n")) (save-buffer) (message "(local variables written.)")) (message "(nothing written.)")) (set (make-local-variable 'TeX-master) master-file)))) (provide 'okular-search)