Scaling Latex previews in Emacs

This is a post in my Latex editing series, an awkward solution to a tiny problem.

The Problem

A common issue with Latex previews in Emacs: The preview images don’t scale when you rescale text in latex-mode or org-mode:

Direct link to video

This makes it cumbersome to work with Latex math in Emacs when dealing with different sized monitors or when presenting from Emacs with Org Tree Slide, for example.

A good solution would be to re-render these fragment images at a higher resolution when applying text scaling. The current version of org-mode (9.52 as of this writing) is very slow to render Latex previews and locks up Emacs for a while, this is not a workable solution.

There are a couple of ways this could be improved: For Org, call an async process to render Latex fragments, or better yet, reuse AucTeX’s preview-latex library for fast, near instant updates. Instant Org previews with preview-latex are now feasible, see the next post in this series. For Latex, adjust the preview-scale variable and regenerate the fragments at each new text scale. These are projects for another day, or for someone more proficient.

The Hack

After asking around for an alternative, I settled on writing a big ol’ hack: Just zoom in on the preview images when increasing the text scale:

Direct link to video

This is fast and cheap, but the downside is that the images get blurry:

Using SVG fragments can mitigate this problem, at least for Org previews:

(setq org-preview-latex-default-process 'dvisvgm) ;No blur when scaling

The Recipe

Preview images are handled using overlays, so the image scaling is done by scanning for and adjusting appropriate overlay properties when changing the text scale:

(defun my/text-scale-adjust-latex-previews ()
  "Adjust the size of latex preview fragments when changing the
buffer's text scale."
  (pcase major-mode
    ('latex-mode
     (dolist (ov (overlays-in (point-min) (point-max)))
       (if (eq (overlay-get ov 'category)
               'preview-overlay)
           (my/text-scale--resize-fragment ov))))
    ('org-mode
     (dolist (ov (overlays-in (point-min) (point-max)))
       (if (eq (overlay-get ov 'org-overlay-type)
               'org-latex-overlay)
           (my/text-scale--resize-fragment ov))))))

(defun my/text-scale--resize-fragment (ov)
  (overlay-put
   ov 'display
   (cons 'image
         (plist-put
          (cdr (overlay-get ov 'display))
          :scale (+ 1.0 (* 0.25 text-scale-mode-amount))))))

(add-hook 'text-scale-mode-hook #'my/text-scale-adjust-latex-previews)