2023-04-01 Showing an image between lines in Emacs

I am now coding a bigger thing in Elisp (and I will definitely blog about it when it’s done), but for now let me say something about an issue I encountered along the way. I needed to display an image between the lines (in the literal sense;-)), preferably not interfering with the editing process – so just inserting two newlines and a space with an image display property inbetween was out of the question. (I wanted the user to be able to edit the buffer irrespective of the image display, and actually inserting anything into the buffer would mess with the undo mechanism).

After some experimenting, I found a solution using overlays. The solution is obvious in hindsight, although it took me some time to arrive to it. (Also, it turned out that I almost reinvented the put-image function.)

Assume that we have two lines in a buffer:

The quick brown fox
jumps over the lazy dog

and we want to put an image of a fox between those lines. (My use case is different, in case anyone’s wondering.) The idea is to go to the the beginning of the second line and create an empty overlay with two properties. One of them is an after-string property with a value of "\n" (that is, a one-character string containing a newline). The second one is a before-string property containing a one-string character (I used a space, but it seems not to matter much) with a text property display set to an image descriptor. This was a bit unexpected – I knew about text properties and that they can be attached to both string and buffer fragments, but I didn’t know that you can use a string with the display text property as the value of the after-string or before-string overlay properties. (This shouldn’t really come as a surprise – in fact, I used precisely this mechanism, but with the face property, in my book about Elisp!)

So, here is a toy example of a function which shows a bitmap “before the current line” (visually) without modifying the buffer. Stay tuned for a real use case in a few weeks!

(defun put-image-before-current-line (image-filename)
  "Insert image from file IMAGE_FILENAME before current line.
Use overlays for that so that the buffer is not modified."
  (save-excursion
    (let ((pos (line-beginning-position)))
      (let ((image-overlay (make-overlay pos pos)))
           (overlay-put image-overlay
                        'before-string
                        (propertize " " 'display (create-image image-filename)))
           (overlay-put image-overlay
                        'after-string
                        "\n")))))

That’s it for today, and until the next time.

CategoryEnglish, CategoryBlog, CategoryEmacs