2

I want to run the following commands when emacs compiles a latex file.

FILE="main.tex"  # currently opened buffer in the emacs
pdflatex -shell-escape -shell-escape -jobname=main_temp $FILE
mv main_temp.pdf main.pdf
open main.pdf

Goal: I am using following pdf editor (https://pdfexpert.com) to read my update tex file's generated pdf in dark mode.

By default when pdflatex -shell-escape -interaction=batchmode current_buffer.tex is executed by emacs, during the compilation of the tex file,the pdf reader shows the following message with a white background for a few seconds, which hurts the eye when switching from black to white and then after compilation from white to black again.

enter image description here

So I just want to trick the pdf-reader to open the compiled pdf when emacs compiles a tex file to a different name and move it to original pdf file when it completes its operation via:

mv paper_temp.pdf paper.pdf
open paper.pdf

minimal.el

(require 'package)
(setq user-init-file (or load-file-name (buffer-file-name)))
(setq user-emacs-directory (file-name-directory user-init-file))

(add-to-list 'package-archives
             '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(setq frame-background-mode 'dark)

(require 'tex)
(add-to-list 'TeX-expand-list
             '("%(mode)"
               (lambda nil
                 (if TeX-interactive-mode ""
                   " -interaction=batchmode"))))

(setq TeX-command-extra-options " -shell-escape")

(defun my-run-latex ()
  "Save all buffers and run LaTeX on the current master."
  (interactive)
  (let* ((inhibit-message t)
         (TeX-command-force "LaTeX")
         (TeX-clean-confirm t)
         (TeX-save-query nil)
         (master (TeX-master-file))
         (process (and (stringp master) (TeX-process master))))
    (TeX-save-document master)
    (when (and (processp process)
               (eq (process-status process) 'run))
      (delete-process process))
    (TeX-command-master)))

(setq LaTeX-item-indent 0)
(setq TeX-auto-save t)
(setq TeX-parse-self t)
(add-hook 'LaTeX-mode-hook 'hl-todo-mode)
(add-hook 'TeX-update-style-hook 'hl-todo-mode)
(add-hook 'LaTeX-mode-hook 'writegood-mode)
(add-hook 'LaTeX-mode-hook (lambda ()
                             (TeX-global-PDF-mode t)))

I'm using my-run-latex() function to compile my tex file.

alper
  • 1,238
  • 11
  • 30

3 Answers3

1

If you use latexmk as your latex command, you can run specific commands when the compile succeeds (or not). latexmk can be used as a wrapper for various latex compilers, for example latexmk -pdflatex=xelatex -pdf %f will use xelatex to generate a pdf.

try adding something like this to the .latexmkrc file

$success_cmd = 'mv %A.pdf %A_temp.pdf && open %A_temp.pdf';

zzkt
  • 456
  • 3
  • 10
1

Secret sauce is TeX-after-compilation-finished-functions.

This uses browse-url-xdg-open to open the file, so associate pdfexpert with .pdf files.


  1. Create a dummy xxx.org
  2. Export it to xxx.tex with C-c C-e l p
  3. Create a test.el file with following contents

;; FILE="paper.tex"  # currently opened buffer in the emacs
;; pdflatex -shell-escape -shell-escape -jobname=paper_temp $FILE;
;; mv paper_temp.pdf paper.pdf;
;; open paper.pdf;

(package-initialize)
(require 'package)

(require 'tex)
(add-to-list 'TeX-expand-list
             '("%(mode)"
               (lambda nil
                 (if TeX-interactive-mode ""
                   " -interaction=batchmode"))))

(defun my-run-latex ()
  "Save all buffers and run LaTeX on the current master."
  (interactive)
  (let* ((TeX-command-extra-options (format " -shell-escape -jobname=%s_temp" (TeX-master-file)))
         (inhibit-message t)
         (TeX-command-force "LaTeX")
         (TeX-clean-confirm t)
         (TeX-save-query nil)
         (master (TeX-master-file))
         (process (and (stringp master) (TeX-process master)))
         (temp-file (expand-file-name (format "%s_temp.pdf" (TeX-master-file)))))
    (TeX-save-document master)
    (when (and (processp process)
               (eq (process-status process) 'run))
      (delete-process process))
    (when (file-exists-p temp-file)
      (delete-file temp-file))
    (TeX-command-master)))

(setq LaTeX-item-indent 0)
(setq TeX-auto-save t)
(setq TeX-parse-self t)

(add-hook 'LaTeX-mode-hook (lambda ()
                             (TeX-global-PDF-mode t)))

(add-hook 'TeX-after-compilation-finished-functions
          (defun my-TeX-after-compilation-finished-functions (file-name)
            (message "compilation finished %s" file-name)
            (let ((temp-file (format "%s_temp.pdf" (file-name-sans-extension file-name))))
              (rename-file temp-file file-name t)
              (browse-url-xdg-open (browse-url-file-url file-name)))))

  1. emacs -Q. Note the use of -Q.
  2. C-x C-f ~/test.el, do M-x eval-buffer
  3. C-x C-f ~/xxx.tex
  4. M-x my-run-latex
  5. Profit!

Additional comment.

When I install the following watch for the dir where ~/xxx_temp.pdf is located

(require 'filenotify)

(defun watch-file-creation (filename)
  (message "Watching for creation of %s" filename)
  (file-notify-add-watch (file-name-directory filename)
             '(change)
             (lambda (event)
               ;; (message "event: %S" event)
               (pcase-let ((`(,descriptor ,action ,file ,file1) event))
                 (message "[%s] %s" action file)))))

(watch-file-creation (expand-file-name "~/xxx_temp.pdf"))

I get

Type ‘C-c C-l’ to display results of compilation.
[changed] /home/whitetrillium/xxx_temp.log [6 times]
[changed] /home/whitetrillium/xxx_temp.aux [2 times]
[changed] /home/whitetrillium/xxx_temp.log [2 times]
[changed] /home/whitetrillium/xxx_temp.out [2 times]
[created] /home/whitetrillium/xxx_temp.pdf [2 times]
[changed] /home/whitetrillium/xxx_temp.toc [2 times]
[changed] /home/whitetrillium/xxx_temp.pdf [2 times]
LaTeX: problems after [0] pages
compilation finished /home/whitetrillium/xxx.pdf
[changed] /home/whitetrillium/xxx_temp.aux [2 times]
[changed] /home/whitetrillium/xxx_temp.pdf [2 times]
[changed] /home/whitetrillium/xxx_temp.log [2 times]
[deleted] /home/whitetrillium/xxx_temp.pdf
[renamed] /home/whitetrillium/xxx_temp.pdf [2 times]
[created] /home/whitetrillium/xxx.pdf

which means that pdf file is getting created, and subsequently modified multiple times.

Note specifically the lines

compilation finished /home/whitetrillium/xxx.pdf
[deleted] /home/whitetrillium/xxx_temp.pdf
[renamed] /home/whitetrillium/xxx_temp.pdf [2 times]
[created] /home/whitetrillium/xxx.pdf

which summarizes what my elisp is doing, which is just what the original poster requested.


  • Instead of using browser to open pdf files, I was opening the pdf file on native pdf editors.I am having a error as follows: error in process sentinel: `Searching for program: No such file or directory, xdg-open` can we use just `open` unix command to open the pdf file, instead of `browse-url-xdg-open`? – alper Jul 12 '22 at 12:19
  • So, don't be confused by `browse` in the name. It opens files with `file:///` url. In my example `xxx.tex` is the file you are compiling and `xxx_temp.pdf` is the file that is passed as `jobname`. The call back is called only on success. So, I will be surprised the file output pdf file doesn't exist. Instead of ` (browse-url-xdg-open (browse-url-file-url file-name)` you can use `(call-process "open" nil nil nil file-name)`. What OS are you running? `xdg-open` is avaiable on Linux. I have tested my change on my local machine with `emacs -Q`. It is unlikely that there is an error ... –  Jul 12 '22 at 12:22
  • I am on macOS, ah `xdg-open` is not installed, seems like it runs inside xQuartz – alper Jul 12 '22 at 12:36
  • You can use `open` instead. Do an `M-: (call-process "open" nil 0 nil "somefile.pdf")` to see if it works for you. –  Jul 12 '22 at 12:38
  • `(call-process "open" nil 0 nil "somefile.pdf")` works! So should I do: `(call-process "open" nil 0 nil filename)` ? – alper Jul 12 '22 at 12:43
1

TeX-after-compilation-finished-functions is a variable defined in tex.el.

Its value is (my-TeX-after-compilation-finished-functions)

Hook being run after TeX/LaTeX/ConTeXt finished successfully. The functions in this hook are run with the DVI/PDF output file given as argument. Using this hook can be useful for updating the viewer automatically after re-compilation of the document.

If you use an emacs-internal viewer such as doc-view-mode or pdf-view-mode, add TeX-revert-document-buffer to this hook.

This variable may be risky if used as a file-local variable.