Justifying LaTeX preview fragments in org-mode

| categories: emacs, orgmode, latex | tags:

A colleague asked if I knew how to center the preview images of LaTeX equations in an org-buffer. This might make org-mode notes look nicer when lecturing, for example. We thought it might be possible to just offset the overlay with a before-string made up of the right number of spaces. I worked out a full solution that lets you "justify" the preview images. You have to add a :justify option to org-format-latex-options, and the option is either 'center or 'right (anything else means left-justified as the default). This will only justify equations that start at the beginning of a line to avoid modifying fragments that are in text. You should see the video to see this in action:

Equation 1: \(e^{i\pi} + 1 = 0\)

An \(x^2 = -1\) equation in the text is not affected.

A display equation with some space after the equation: \[e^{i \cdot \pi} + 1 = 0\]

This is a numbered equation.

\begin{equation} \int x^2 dx \end{equation}

The idea is pretty simple, we get the width of the window, and the width of the image, and compute the offset that approximately centers or right justifies the overlay, and then add the before-string property to the overlay. While we are at it, I will add a tooltip to the image so you can see the LaTeX code that created it, and make it clickable so you can toggle it back to the code. I apply the functions as after advice to the function that creates the overlay, so we do not have to adapt the org code at all. Here is the code that does it.

;; specify the justification you want
(plist-put org-format-latex-options :justify 'center)

(defun org-justify-fragment-overlay (beg end image imagetype)
  "Adjust the justification of a LaTeX fragment.
The justification is set by :justify in
`org-format-latex-options'. Only equations at the beginning of a
line are justified."
  (cond
   ;; Centered justification
   ((and (eq 'center (plist-get org-format-latex-options :justify)) 
         (= beg (line-beginning-position)))
    (let* ((img (create-image image 'imagemagick t))
           (width (car (image-size img)))
           (offset (floor (- (/ (window-text-width) 2) (/ width 2)))))
      (overlay-put (ov-at) 'before-string (make-string offset ? ))))
   ;; Right justification
   ((and (eq 'right (plist-get org-format-latex-options :justify)) 
         (= beg (line-beginning-position)))
    (let* ((img (create-image image 'imagemagick t))
           (width (car (image-display-size (overlay-get (ov-at) 'display))))
           (offset (floor (- (window-text-width) width (- (line-end-position) end)))))
      (overlay-put (ov-at) 'before-string (make-string offset ? ))))))

(defun org-latex-fragment-tooltip (beg end image imagetype)
  "Add the fragment tooltip to the overlay and set click function to toggle it."
  (overlay-put (ov-at) 'help-echo
               (concat (buffer-substring beg end)
                       "mouse-1 to toggle."))
  (overlay-put (ov-at) 'local-map (let ((map (make-sparse-keymap)))
                                    (define-key map [mouse-1]
                                      `(lambda ()
                                         (interactive)
                                         (org-remove-latex-fragment-image-overlays ,beg ,end)))
                                    map)))

;; advise the function to a
(advice-add 'org--format-latex-make-overlay :after 'org-justify-fragment-overlay)
(advice-add 'org--format-latex-make-overlay :after 'org-latex-fragment-tooltip)

That is it. If you get tired of the advice, remove it like this:

(advice-remove 'org--format-latex-make-overlay 'org-justify-fragment-overlay)
(advice-remove 'org--format-latex-make-overlay 'org-latex-fragment-tooltip)

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 9.0

Discuss on Twitter