My Technical Notes

Friday, 5 August 2016

Pasting an Image into Org Mode on Windows

The following is the answer I posted on StackOverflow.


My solution is for the Windows platform. Many thanks to Assem for giving his solution on which I based mine.

I wrote a C# console app `CbImage2File` that writes the image on the clipboard (if there is one) to a path given as the command line argument. You will need a reference to System.Windows.Forms assembly.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CbImage2File
{
    class Program
    {
        private static int FAILURE = 1;

        private static void Failure(string description)
        {
            Console.Error.WriteLine("Clipboard does not contain image data");
            Environment.Exit(FAILURE);
        }

        [STAThread]
        static void Main(string[] args)
        {
            var image = System.Windows.Forms.Clipboard.GetImage();

            if (image == null)
            {
                Failure("Clipboard does not contain image data");
            }

            if (args.Length == 0)
            {
                Failure("You have not specified an output path for the image");
            }

            if (args.Length > 1)
            {
                Failure("You must only specify a file path (1 argument)");
            }

            var filePath = args[0];
            var folderInfo = new System.IO.FileInfo(filePath).Directory;

            if (!folderInfo.Exists)
            {
                System.IO.Directory.CreateDirectory(folderInfo.FullName);
            }

            image.Save(filePath);
        }
    }
}

I use Greenshot to take the screenshot, which (via its configuration) automatically copies the screenshot to the clipboard.

I then use a shortcut `C-S-v` to paste the image in the org mode buffer using the function `org-insert-image-from-clipboard`:


(defun org-insert-image-from-clipboard ()
  (interactive)
  (let* ((the-dir (file-name-directory buffer-file-name))
  (attachments-dir (concat the-dir "attachments"))
  (png-file-name (format-time-string "%a%d%b%Y_%H%M%S.png"))
  (png-path (concat attachments-dir "/" png-file-name))
  (temp-buffer-name "CbImage2File-buffer"))
    (call-process "C:/_code/CbImage2File/CbImage2File/bin/Debug/CbImage2File.exe" nil temp-buffer-name nil png-path)
    (let ((result (with-current-buffer temp-buffer-name (buffer-string))))
      (progn
 (kill-buffer temp-buffer-name)
 (if (string= result "")
     (progn 
       (insert (concat "[[./attachments/" png-file-name "]]"))
       (org-display-inline-images))
   (insert result))))))

(define-key org-mode-map (kbd "C-S-v") 'org-insert-image-from-clipboard)

This function will work out a file path, then invoke the C# app to create `png` file, then it will add the image tag and then call `org-display-inline-images` to show the image. If there is no image on the clipboard, it will paste in the response from the C# app.

1 comment:

w0wt1p said...

Thanks a lot for this post and the code. It makes my documentation job 10x easier! :)

/Johan