Displaying image overlays on image filenames in Emacs

| categories: emacs, orgmode | tags:

It has always bothered me a little that I have to add a file image after code blocks in org-mode to see the results. That extra work… I also don't like having to explicitly print the figure in the code, since that is the extra work, just in a different place. Today I look into two approaches to this. First, we consider something like tooltips, and second just putting overlays of image files right on the file name. The plus side of this is no extra work. The downside is they won't export; that will still take the extra work, but you needed that for the caption anyway for now.

Here is a video illustrating the code in this post: https://www.youtube.com/watch?v=VuAnwCERM0U

Here is a test.

import matplotlib.pyplot as plt
plt.plot([0, 1, 2, 4, 16])
plt.savefig("test-fig.png")

1 Tooltip approach

Building on our previous approach of graphical tooltips, we try that here to show the images. I have solved the issue of why the images didn't show in the tooltips before; it was related to how Emacs was built. I used to build it with "cocoa" support so it integrates well in OSX. Here, I have build it with gtk3, and the tooltips work with images.

(defvar image-tooltip-re (concat  "\\(?3:'\\|\"\\)\\(?1:.*\\."
                                  (regexp-opt '("png" "PNG" "JPG" "jpeg"
                                                "jpg" "JPEG" "eps" "EPS"))
                                  "\\)\\(?:\\3\\)")
  "Regexp to match image filenames in quotes")

(defun image-tooltip (window object position)
  (save-excursion
    (goto-char position)
    (let (beg end imgfile img s)
      (while (not (looking-at image-tooltip-re))
        (forward-char -1))
      (setq imgfile (match-string-no-properties 1))
      (when (file-exists-p imgfile)
        (setq img (create-image (expand-file-name imgfile)
                                'imagemagick nil :width 200))
        (propertize "Look in the minibuffer"
                    'display img)))))

(font-lock-add-keywords
 nil
 `((,image-tooltip-re
    0 '(face font-lock-keyword-face
             help-echo image-tooltip))))

(font-lock-fontify-buffer)

Now these both have tooltips on them: "test-fig.png" and 'test-fig.png'.

2 The overlay approach

We might alternatively prefer to put overlays in the buffer. Here we make that happen.

(defun next-image-overlay (&optional limit)
  (when (re-search-forward image-tooltip-re limit t)
    (setq beg (match-beginning 0)
          end (match-end 0)
          imgfile (match-string 1))
    (when (file-exists-p imgfile)
      (setq img (create-image (expand-file-name imgfile)
                              'imagemagick nil :width 300))
      (setq ov (make-overlay beg end))
      (overlay-put ov 'display img)
      (overlay-put ov 'face 'default)
      (overlay-put ov 'org-image-overlay t)
      (overlay-put ov 'modification-hooks
                   (list 'org-display-inline-remove-overlay)))))

(font-lock-add-keywords
 nil
 '((next-image-overlay (0  'font-lock-keyword-face t)))
 t)

Here is the example we looked at before.

import matplotlib.pyplot as plt
plt.plot([-0, 1, 2, 4, 16])
plt.savefig("test-fig.png")

You may want to remove those overlays. Here is one way. Note they come back if you don't disable the font-lock keywords though.

(ov-clear 'org-image-overlay)

I know you want to do that so here is:

(font-lock-remove-keywords
 nil
 '((next-image-overlay (0  'font-lock-keyword-face t))))

(ov-clear 'org-image-overlay)

Note you still have to clear the overlays. Font lock doesn't seem to do that for you I think.

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

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter