DEV Community

Alex Rampp
Alex Rampp

Posted on

How to paste images into Emacs org-mode running in Windows Subsystem for Linux

I organize myself by implementing Getting Things Done using plain text files processed by Emacs org-mode. Since Emacs origins from the Unix eco system, there are some obstacles when running on Windows. One of it is pasting images from the clipboard.

My Environment

I am running GNU Emacs version 27.1 with the configuration framework Doom on a Windows 10 machine within Windows Subsystem for Linux 2 (WSL2). I use the Ubuntu Linux 20.04 LTS distribution and integrate the Emacs GUI into my windows desktop using the VcXsrv Windows X Server in version 1.20.8.1. Furthermore I have PowerShell version 5.1 (default shipped with Windows 10).

Problem

During work, I often take screenshots using the Windows clipboard tool and paste them directly into my notes. In org-mode it is possible link images from a text file and display them within Emacs and there are existing solutions to paste and display images directly into org files. Unfortunately, VcXsrv does not support sharing images via clipboard (there is an open feature request) so all the existing solutions won’t work.

Solution

In WSL2, it is possible to launch windows applications from the linux shell. Hence it is possible to call PowerShell and leveraging its scripting capabilities. It turns out, that saving an image from the clipboard can be achieved with the following one liner:

(Get-Clipboard -Format image).Save('somefilename.png')
Enter fullscreen mode Exit fullscreen mode

With the help of this, I was able to assemble the following lisp function to get the job done:

(defun my-org-paste-image ()
  "Paste an image into a time stamped unique-named file in the
same directory as the org-buffer and insert a link to this file."
  (interactive)
  (let* ((target-file
          (concat
           (make-temp-name
            (concat (buffer-file-name)
                    "_"
                    (format-time-string "%Y%m%d_%H%M%S_"))) ".png"))
         (wsl-path
          (concat (as-windows-path(file-name-directory target-file))
                  "\\"
                  (file-name-nondirectory target-file)))
         (ps-script
          (concat "(Get-Clipboard -Format image).Save('" wsl-path "')")))

    (powershell ps-script)

    (if (file-exists-p target-file)
        (progn (insert (concat "[[" target-file "]]"))
               (org-display-inline-images))
      (user-error
       "Error pasting the image, make sure you have an image in the clipboard!"))
    ))

(defun as-windows-path (unix-path)
  "Takes a unix path and returns a matching WSL path
(e.g. \\\\wsl$\\Ubuntu-20.04\\tmp)"
  ;; substring removes the trailing \n
  (substring
   (shell-command-to-string
    (concat "wslpath -w " unix-path)) 0 -1))

(defun powershell (script)
  "executes the given script within a powershell and returns its return value"
  (call-process "powershell.exe" nil nil nil
                "-Command" (concat "& {" script "}")))
Enter fullscreen mode Exit fullscreen mode

First, it creates a variable called target-file which is an absolute Unix path to the file that should be created (e.g. ’/home/user/test.png’). The challenge is, that PowerShell cannot access this path since it’s only valid within the Unix file system. Luckily, WSL2 maps the Unix file system into windows defining the network path \wsl$\. It also provides the utility wslpath to convert paths back and forth between Unix and Windows conventions. Leveraging this, we can define the variable wsl-path, which is the Windows-compatible version of target-file and the resulting ps-script we have to hand over to PowerShell.

Finally we call PowerShell with ps-script which results in a new png file located at target-path on success. If it was successful, we insert it as a link into the current document and display it as inline image. If the file was not created (this usually happens when there was no image in the clipboard), we’ll display an error message to the user.

Conclusion

Leveraging PowerShell in a WSL2 system seems to be really powerful. With a few lines of lisp, it is possible to call Windows commands directly from Emacs. This fact opens a whole space of new possibilities on integrating Emacs (and org-mode) on Windows systems.

This article has been published originally on my personal blog.

Top comments (0)