org-latex-preview: Set up and troubleshooting

Table of Contents

NOTE: These instructions were last updated on Monday, 2024-05-13, so they might be out of date.


org-latex-preview is a rewrite of Org’s LaTeX preview system. There is a demo here:

This page covers the installation and set up of this package (and version of Org mode).

Installation

After this feature is merged into Org, the installation step should be unnecessary.

You can install it from https://git.tecosaur.net/tec/org-mode.

How to install it depends on how you use install packages in Emacs.

Note: In all cases, you need to ensure that this new Org version is available before any packages that depend on Org are loaded. Typically this means placing the declaration/configuration of this Org package before everything else in your init file.

Regular Emacs, with package-install

  1. Uninstall any version of Org you might have that’s not the one built into Emacs: M-x package-delete ⮐ org. This might give you some trouble because of packages that depend on org. If you’re using the built-in Org library, you can skip this step.
  2. If your Emacs uses native compilation (native-comp-available-p), delete the eln-cache for Org files. These should be in one of the directories in the list native-comp-eln-load-path.
  3. Install the above version of Org with package-vc-install. Run

       (package-vc-install '(org-mode :url "https://code.tecosaur.net/tec/org-mode"))
    

    Note: We call the package org-mode and not org, otherwise we wouldn’t be able to delete it afterwards with package-delete due to dependencies of other packages on org.

  4. Locate the installation path (~/.emacs.d/elpa/org-mode/lisp) or equivalent, and run make inside the directory.
  5. In your configuration file, you can load Org via

       (use-package org :load-path "~/.emacs.d/elpa/org-mode/lisp/")
    

Regular Emacs with Straight

NOTE: You can skip the first step if you use the built-in version of Org mode.

  1. Delete the org directory that Straight uses for Org. If you don’t know this, run M-x list-load-path-shadows, look for org.el or any other org-* feature. Then delete the repo that file is in.
  2. If your Emacs uses native compilation (native-comp-available-p), delete the eln-cache for Org files. These should be in one of the directories in the list native-comp-eln-load-path.
  3. Use the following recipe:

    (use-package org
      :defer
      :straight `(org
                  :fork (:host nil
                         :repo "https://git.tecosaur.net/tec/org-mode.git"
                         :branch "dev"
                         :remote "tecosaur")
                  :files (:defaults "etc")
                  :build t
                  :pre-build
                  (with-temp-file "org-version.el"
                   (require 'lisp-mnt)
                   (let ((version
                          (with-temp-buffer
                            (insert-file-contents "lisp/org.el")
                            (lm-header "version")))
                         (git-version
                          (string-trim
                           (with-temp-buffer
                             (call-process "git" nil t nil "rev-parse" "--short" "HEAD")
                             (buffer-string)))))
                    (insert
                     (format "(defun org-release () \"The release version of Org.\" %S)\n" version)
                     (format "(defun org-git-version () \"The truncate git commit hash of Org mode.\" %S)\n" git-version)
                     "(provide 'org-version)\n")))
                  :pin nil))
    

    Place this recipe near the beginning of your config, before any packages that depend on Org are configured.

  4. Quit and restart Emacs. You can also run straight-check-package, straight-pull-package and straight-rebuild-package with org as the argument.

Regular Emacs with Elpaca

NOTE: You can skip the first step if you use the built-in version of Org mode.

  1. Delete the org directory that Elpaca uses for Org. If you don’t know this, run M-x list-load-path-shadows, look for org.el or any other org-* feature. Then delete the repo that file is in.
  2. If your Emacs uses native compilation (native-comp-available-p), delete the eln-cache for Org files. These should be in one of the directories in the list native-comp-eln-load-path.
  3. Use the following recipe:

    (use-package org
      :defer
      :ensure `(org
                :remotes ("tecosaur"
                          :repo "https://git.tecosaur.net/tec/org-mode.git"
                          :branch "dev")
                :files (:defaults "etc")
                :build t
                :pre-build
                (with-temp-file "org-version.el"
                 (require 'lisp-mnt)
                 (let ((version
                        (with-temp-buffer
                          (insert-file-contents "lisp/org.el")
                          (lm-header "version")))
                       (git-version
                        (string-trim
                         (with-temp-buffer
                           (call-process "git" nil t nil "rev-parse" "--short" "HEAD")
                           (buffer-string)))))
                  (insert
                   (format "(defun org-release () \"The release version of Org.\" %S)\n" version)
                   (format "(defun org-git-version () \"The truncate git commit hash of Org mode.\" %S)\n" git-version)
                   "(provide 'org-version)\n")))
                :pin nil))
    

    Place this recipe near the beginning of your config, before any packages that depend on Org are configured.

  4. Quit and restart Emacs.

Doom Emacs

NOTE: You can skip the first step if you use the built-in version of Org mode.

  1. Delete the org directory that Straight uses for Org. If you don’t know this, run M-x list-load-path-shadows, look for org.el or any other org-* feature. Then delete the repo that file is in.
  2. If your Emacs uses native compilation (native-comp-available-p), delete the eln-cache for Org files. These should be in one of the directories in the list native-comp-eln-load-path.
  3. Use the following recipe:

    (package! org :recipe
      (:host nil :repo "https://git.tecosaur.net/mirrors/org-mode.git" :remote "mirror" :fork
             (:host nil :repo "https://git.tecosaur.net/tec/org-mode.git" :branch "dev" :remote "tecosaur")
             :files
             (:defaults "etc")
             :build t :pre-build
             (with-temp-file "org-version.el"
               (require 'lisp-mnt)
               (let
                   ((version
                     (with-temp-buffer
                       (insert-file-contents "lisp/org.el")
                       (lm-header "version")))
                    (git-version
                     (string-trim
                      (with-temp-buffer
                        (call-process "git" nil t nil "rev-parse" "--short" "HEAD")
                        (buffer-string)))))
                 (insert
                  (format "(defun org-release () \"The release version of Org.\" %S)\n" version)
                  (format "(defun org-git-version () \"The truncate git commit hash of Org mode.\" %S)\n" git-version)
                  "(provide 'org-version)\n"))))
      :pin nil)
    
    (unpin! org)
    

    Place this before your package declarations for other org-related packages.

  4. Quit Emacs, run doom sync -u or whatever you madlads do.

Spacemacs, Centaur Emacs, I use Quelpa

I dunno, sorry. I’m sure one of the above sets of instructions will clue you in.

Set up

org-latex-preview can do some nifty things, like live-previews:

To explore the options, run M-x customize-group ⮐ org-latex-preview. Most settings you’re looking for are probably covered.

Sample use-package declaration

(use-package org-latex-preview
  :config
  ;; Increase preview width
  (plist-put org-latex-preview-appearance-options
             :page-width 0.8)

  ;; Use dvisvgm to generate previews
  ;; You don't need this, it's the default:
  (setq org-latex-preview-process-default 'dvisvgm)
  
  ;; Turn on auto-mode, it's built into Org and much faster/more featured than
  ;; org-fragtog. (Remember to turn off/uninstall org-fragtog.)
  (add-hook 'org-mode-hook 'org-latex-preview-auto-mode)

  ;; Block C-n and C-p from opening up previews when using auto-mode
  (add-hook 'org-latex-preview-auto-ignored-commands 'next-line)
  (add-hook 'org-latex-preview-auto-ignored-commands 'previous-line)

  ;; Enable consistent equation numbering
  (setq org-latex-preview-numbered t)

  ;; Bonus: Turn on live previews.  This shows you a live preview of a LaTeX
  ;; fragment and updates the preview in real-time as you edit it.
  ;; To preview only environments, set it to '(block edit-special) instead
  (setq org-latex-preview-live t)

  ;; More immediate live-previews -- the default delay is 1 second
  (setq org-latex-preview-live-debounce 0.25))

Bonus: Consistently centered preview imges

Include the following code in your config and turn on org-latex-preview-center-mode to center displaymath previews in Org buffers.

;; code for centering LaTeX previews -- a terrible idea
(use-package org-latex-preview
  :config
  (defun my/org-latex-preview-uncenter (ov)
    (overlay-put ov 'before-string nil))
  (defun my/org-latex-preview-recenter (ov)
    (overlay-put ov 'before-string (overlay-get ov 'justify)))
  (defun my/org-latex-preview-center (ov)
    (save-excursion
      (goto-char (overlay-start ov))
      (when-let* ((elem (org-element-context))
                  ((or (eq (org-element-type elem) 'latex-environment)
                       (string-match-p "^\\\\\\[" (org-element-property :value elem))))
                  (img (overlay-get ov 'display))
                  (prop `(space :align-to (- center (0.55 . ,img))))
                  (justify (propertize " " 'display prop 'face 'default)))
        (overlay-put ov 'justify justify)
        (overlay-put ov 'before-string (overlay-get ov 'justify)))))
  (define-minor-mode org-latex-preview-center-mode
    "Center equations previewed with `org-latex-preview'."
    :global nil
    (if org-latex-preview-center-mode
        (progn
          (add-hook 'org-latex-preview-overlay-open-functions
                    #'my/org-latex-preview-uncenter nil :local)
          (add-hook 'org-latex-preview-overlay-close-functions
                    #'my/org-latex-preview-recenter nil :local)
          (add-hook 'org-latex-preview-overlay-update-functions
                    #'my/org-latex-preview-center nil :local))
      (remove-hook 'org-latex-preview-overlay-close-functions
                    #'my/org-latex-preview-recenter)
      (remove-hook 'org-latex-preview-overlay-update-functions
                    #'my/org-latex-preview-center)
      (remove-hook 'org-latex-preview-overlay-open-functions
                    #'my/org-latex-preview-uncenter))))

Troubleshooting

“Variable org-FOO is void” at startup or when I open an Org file

If you get errors like the above, you have a “mixed install”, i.e. there are two versions of Org active and Emacs is confused. Make sure the older version of Org is wiped clean, see the above section.

Precompilation errors

If you get a warning like the following:

⛔ Warning (org): Precompile failed. Disabling LaTeX preview precompile in this buffer. To re-enable, run `(setq-local org-latex-preview-process-precompiled t)’ or reopen this buffer.

  • You can continue to use org-latex-preview, it will just be slower. (Precompilation speeds up preview generation.)
  • To investigate the cause, check the *Org LaTeX Precompilation* buffer. (Something in your preamble is causing the precompilation to fail.)

No errors until I try to generate a preview, then all hell breaks loose

Needless to say, you need a TeX distribution installed and available to Emacs. You need some commonly included LaTeX packages too.

Evaluate and run the function at this gist, then check the output to see if something looks off. The code is also reproduced here:

Check health of org-latex-preview
(defun org-latex-preview-check-health (&optional inter)
  "Inspect the relevent system state and setup.
INTER signals whether the function has been called interactively."
  (interactive (list t))
  ;; Collect information
  (let* ((diag `(:interactive ,inter)))
    (plist-put diag :org-version org-version)
    ;; modified variables
    (plist-put diag :modified
               (let ((list))
                 (mapatoms
                  (lambda (v)
                    (and (boundp v)
                         (string-match "\\`\\(org-latex-\\|org-persist-\\)" (symbol-name v))
                         (or (and (symbol-value v)
                                  (string-match "\\(-hook\\|-function\\)\\'" (symbol-name v)))
                             (and
                              (get v 'custom-type) (get v 'standard-value)
                              (not (equal (symbol-value v)
                                          (eval (car (get v 'standard-value)) t)))))
                         (push (cons v (symbol-value v)) list))))
                 list))
    ;; Executables
    ;; latex processors
    (dolist (processor org-latex-compilers)
      (when-let ((path (executable-find processor)))
        (let ((version (with-temp-buffer
                         (thread-last
                           (concat processor " --version")
                           (shell-command-to-string)
                           (insert))
                         (goto-char (point-min))
                         (buffer-substring (point) (line-end-position)))))
          (push (list processor version path) (plist-get diag :latex-processors)))))
    ;; Image converters
    (dolist (converter '("dvipng" "dvisvgm" "convert"))
      (when-let ((path (executable-find converter)))
        (let ((version (with-temp-buffer
                         (thread-last
                           (concat converter " --version")
                           (shell-command-to-string)
                           (insert))
                         (goto-char (point-min))
                         (buffer-substring (point) (line-end-position)))))
          (push (list converter version path) (plist-get diag :image-converters)))))
    (when inter
      (with-current-buffer (get-buffer-create "*Org LaTeX Preview Report*")
        (let ((inhibit-read-only t))
          (erase-buffer)

          (insert (propertize "Your LaTeX preview process" 'face 'outline-1))
          (insert "\n\n")

          (let* ((latex-available (cl-member org-latex-compiler
                                             (plist-get diag :latex-processors)
                                             :key #'car :test #'string=))
                 (precompile-available
                  (and latex-available
                       (not (member org-latex-compiler '("lualatex" "xelatex")))))
                 (proc-info (alist-get
                             org-latex-preview-default-process
                             org-latex-preview-process-alist))
                 (image-converter (cadr (plist-get proc-info :programs)))
                 (image-converter
                  (cl-find-if
                   (lambda (c)
                     (string= image-converter c))
                   (plist-get diag :image-converters)
                   :key #'car))
                 (image-output-type (plist-get proc-info :image-output-type)))
            (if org-latex-preview-precompile
                (insert "Precompile with "
                        (propertize (map-elt org-latex-precompile-compiler-map
                                             org-latex-compiler)
                                    'face
                                    (list
                                     (if precompile-available
                                         '(:inherit success :box t)
                                       '(:inherit error :box t))
                                     'org-block))
                        " → "))
            (insert "LaTeX Compile with "
                    (propertize org-latex-compiler 'face
                                (list
                                 (if latex-available
                                     '(:inherit success :box t)
                                   '(:inherit error :box t))
                                 'org-block))
                    " → ")
            (insert "Convert to "
                    (propertize (upcase image-output-type) 'face '(:weight bold))
                    " with "
                    (propertize (car image-converter) 'face
                                (list
                                 (if image-converter
                                     '(:inherit success :box t)
                                   '(:inherit error :box t))
                                 'org-block))
                    "\n\n")
            (insert (propertize org-latex-compiler 'face 'outline-3)
                    "\n"
                    (if latex-available
                        (concat
                          (propertize
                           (mapconcat #'identity (map-nested-elt diag `(:latex-processors ,org-latex-compiler))
                                      "\n")
                           'face 'org-block)
                          "\n"
                          (when (and latex-available (not precompile-available))
                            (propertize
                             (format "\nWarning: Precompilation not available with %S!\n" org-latex-compiler)
                             'face 'warning)))
                      (propertize "Not found in path!\n" 'face 'error))
                    "\n")
            
            (insert (propertize (cadr (plist-get proc-info :programs)) 'face 'outline-3)
                    "\n"
                    (if image-converter
                        (propertize
                         (concat
                          (mapconcat #'identity (cdr image-converter) "\n")
                          "\n")
                         'face 'org-block)
                      (propertize "Not found in path!\n" 'face 'error))
                    "\n")
            ;; dvisvgm version check
            (when (equal (car-safe image-converter)
                         "dvisvgm")
              (let* ((version-string (cadr image-converter))
                     (dvisvgm-ver (progn
                                    (string-match "\\([0-9.]+\\)" version-string)
                                    (match-string 1 version-string))))

                (when (version< dvisvgm-ver "3.0")
                  (insert (propertize
                           (format "Warning: dvisvgm version %s < 3.0, displaymath will not be centered."
                                   dvisvgm-ver)
                           'face 'warning)
                          "\n\n"))))
            (when (not (and latex-available image-converter))
              (insert "path: " (getenv "PATH") "\n\n")))
          ;; Settings
          (insert (propertize "LaTeX preview options" 'face 'outline-2)
                  "\n")

          (pcase-dolist (`(,var . ,msg)
                         `((,org-latex-preview-precompile       . "Precompilation           ")
                           (,org-latex-preview-numbered . "Equation renumbering     ")
                           (,org-latex-preview-persist  . "Caching with org-persist ")))
            (insert (propertize "• " 'face 'org-list-dt)
                    msg
                    (if var
                        (propertize "ON" 'face '(success bold org-block))
                      (propertize "OFF" 'face '(error bold org-block)))
                    "\n"))
          (insert "\n"
                  (propertize "LaTeX preview sizing" 'face 'outline-2) "\n"
                  (propertize "•" 'face 'org-list-dt)
                  " Page width  "
                  (propertize
                   (format "%S" org-latex-preview-width)
                   'face '(org-code org-block))
                  "   (display equation width in LaTeX)\n"
                  (propertize "•" 'face 'org-list-dt)
                  " Scale       "
                  (propertize
                   (format "%.2f" (plist-get org-latex-preview-options :scale))
                   'face '(org-code org-block))
                  "  (PNG pixel density multiplier)\n"
                  (propertize "•" 'face 'org-list-dt)
                  " Zoom        "
                  (propertize
                   (format "%.2f" (plist-get org-latex-preview-options :zoom))
                   'face '(org-code org-block))
                  "  (display scaling factor)\n\n")
          (insert (propertize "LaTeX preview preamble" 'face 'outline-2) "\n")
          (let ((major-mode 'org-mode))
            (let ((point-1 (point)))
              (insert org-latex-preview-preamble "\n")
              (org-src-font-lock-fontify-block 'latex point-1 (point))
              (add-face-text-property point-1 (point) '(:inherit org-block :height 0.9)))
            (insert "\n")
            ;; Diagnostic output
            (insert (propertize "Diagnostic info (copied)" 'face 'outline-2)
                    "\n\n")
            (let ((point-1 (point)))
              (pp diag (current-buffer))
              (org-src-font-lock-fontify-block 'emacs-lisp point-1 (point))
              (add-face-text-property point-1 (point) '(:height 0.9))))
          (gui-select-text (prin1-to-string diag))
          (special-mode))
        (setq-local
         revert-buffer-function
         (lambda (&rest _)
           (call-interactively #'org-latex-preview-check-health)
           (message "Refreshed LaTeX preview diagnostic")))
        (let ((message-log-max nil))
          (toggle-truncate-lines 1))
        (goto-char (point-min))
        (display-buffer (current-buffer))))
    diag))


HTML exports are breaking when I use tex:dvisvgm or tex:dvipng

This could be a few different things, but if you get errors about org-html-format-latex being missing, find this code in the org-compat library and evaluate it manually. For anything else contact us.

Previews are generated but the sizes are off

Getting help

A few different options

Date: 2024-01-03 Wed 00:00

Author: Karthink

Created: 2024-05-13 Mon 10:36

Validate