Integrating graphics with text in Emacs
I discussed Emacs's artist-mode a few days ago as a simple, but incomplete, solution to the problem of sketching graphs while taking notes during a math class. But I've found a much better way, one that allows for including any images -- drawings, photos, or screenshots. It took a little work and some custom .emacs code, but I love the result.
Iimage mode
The key is iimage-mode, which displays inline images. In this mode, you put a line in your buffer with a reference to your image file, something like this:
file://myimage.jpgand Emacs will replace it with the contents of that image. Marvellous!
You can use other patterns for filenames as well, but I'm fine with using URLs. Note there are only two slashes in file:// -- it's a local file in the same directory as the text file being edited.
It's a little tricky to enable it.
The docs are not entirely clear on the differences between
iimage-mode
, turn-on-iimage-mode
and iimage-mode-buffer
.
I found I could get a file that already had existing images to display
them with:
(turn-on-iimage-mode) (iimage-mode-buffer t)
Very cool! But too much to type every time. And to use it for note-taking, I needed a way to say, "Create a new image here, let me edit it, then display the image I just edited inline."
Enabling iimage-mode automatically
First, I wanted iimage mode displayed automatically on files in my note-taking directories. I normally use text-mode for these files, with spell checking and line wrapping turned on (auto-fill mode). So I defined a new minor mode based on text-mode:
(define-derived-mode text-img-mode text-mode "Image display mode" (auto-fill-mode) (turn-on-iimage-mode) (iimage-mode-buffer t) )
Then I wanted this mode to be called whenever I'm editing a file
in my classes directory. So I added it to my auto-mode-alist
:
(setq auto-mode-alist ... (cons '("Docs/classes/" . text-img-mode) ... auto-mode-alist) )
Inserting a new image
Next, I needed a way to insert an image URL into the buffer and call up an image editor on it. I shouldn't have to type the filename twice and keep track of it; that's what computers are for.
And I needed a drawing program. As a longtime GIMP geek, most of my computer drawing has been in GIMP. But GIMP is overkill for calling up a quick sketch window. I was tempted to use TuxPaint; it's a good sketching app even if you're not five years old, and it's fun and easy to use. But by default, TuxPaint has some features that get in the way of note-taking, like distracting sound effects. I'm sure it's possible to turn those off, and I do plan to investigate that.
I saw a reference to pinta as a lightweight drawing app, but it required a boatload of Mono libraries that I don't otherwise need, and Krita has the same problem with KDE services. So I opted for MyPaint. It works okay, though it's rather slow to start up and has some other issues, so I'm still hoping to find a more lightweight sketching app.
In any case, I fiddled around with start-process
until
I figured out how to use it to start a program. Then I wrote a little
function that lets the user pick a filename, inserts a URL to that
filename into the buffer, then calls up mypaint on the file.
(defun img () "Prompt for a filename, then call up mypaint to create an image" (interactive) (let ((imgfile (read-string "Filename? " "xxx.jpg" 'my-history))) (insert "\nfile://" imgfile "\n" ) (start-process "mypaint" nil "/usr/bin/mypaint" imgfile) ))
Worked fine! I can run M-x img
, be prompted for a filename,
and get a mypaint window where I can make my sketch.
Noticing that a new image has been added
But wait. I finish sketching, write the file and quit mypaint ...
and the buffer still shows something like
file://xxx.jpg
, even if it's showing other images
inline. I needed a way to tell it to refresh and load any new images.
(I considered having emacs wait for mypaint to exit, but decided I
might sometimes want to keep editing while mypaint was still up.)
M-x eval-expression (iimage-mode-buffer t)
will do that,
but that's a lot of typing to do. Obviously, I needed a key binding.
Strangely enough, C-c i wasn't taken for text buffers, so that seemed
like a natural. So I added a key binding to the end of the
text-img-mode
. iimage-mode-buffer
requires
that t argument -- it gives an error without it -- so the key binding
looks a little more complicated than one that just calls a simple function.
I added it to the end of my text-img-mode function.
(define-derived-mode text-img-mode text-mode "Image display mode" ... (local-set-key "\C-ci" (lambda () (interactive) (iimage-mode-buffer t))) )
But after using it a bit, I discovered that this didn't reload images if I edited them a second time. Fortunately, vwood had the answer:
(defun refresh-iimages () "Only way I've found to refresh iimages (without also recentering)" (interactive) (clear-image-cache nil) (iimage-mode nil) (iimage-mode t) (message "Refreshed images") )
I added the message
at the end, since otherwise the function
left a distracting
"Toggling iimage-mode off; better pass an explicit argument" error.
Then the key binding in my text-img-mode became
(local-set-key "\C-ci" 'refresh-iimages)
Inserting a screenshot
Wait -- one more thing. As I actually used text-img-mode to take notes, I discovered that taking screenshots would actually be much more useful than making my own drawings. Then I could copy small sections of the slides and graphs into my notes at the appropriate place, without needing to copy equations at all.
Why not write a function to allow that? The unpleasantly named scrot program fills the bill nicely, and gives me a choice of clicking in a window or dragging out an area of the screen.
(defun screenshot () "Prompt for a filename, then call up scrot to create an interactive screenshot" (interactive) (let ((imgfile (read-string "Filename? " "scr.jpg" 'my-history))) (insert "\nfile://" imgfile "\n" ) (start-process "scrot" nil "/usr/bin/scrot" "-s" imgfile) ))
This turned out to be so useful that I added a key for it in text-img-mode:
(local-set-key "\C-cs" 'screenshot)
I'm so happy with the result! Iimage mode is working great, and having text and images together is turning out to be perfect for note-taking.
My only problem now -- okay, I admit it -- is a tendency to get so
excited over inserting screenshots that I get distracted and forget to
actually listen to the lecture. I'm sure I'll get over that, but for
now, Thank goodness vlc is good at skipping back!
[ 13:42 Jan 12, 2013 More linux/editors | permalink to this entry | ]