Paste an image on clipboard to Emacs Org mode file

2020-05-13 20:27发布

As I'm using the Emacs Org mode as a research log, sometime I want to keep track of something via screenshot images, and I definitely don't want to save them. So I'm wondering is there any way to insert those figures into my org mode file, like with word coping them from clipboard?

8条回答
Root(大扎)
2楼-- · 2020-05-13 20:48

For Windows 10 users, I modified the answer provided by @assem to work with out-of-the-box tools:

(defun my-org-screenshot ()
  "Take a screenshot into a time stamped unique-named file in the
   same directory as the org-buffer and insert a link to this file."  
   (interactive)
   (setq filename
     (concat
       (make-temp-name
         (concat (buffer-file-name)
              "_"
              (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
   (shell-command "snippingtool /clip")
   (shell-command (concat "powershell -command \"Add-Type -AssemblyName System.Windows.Forms;if ($([System.Windows.Forms.Clipboard]::ContainsImage())) {$image = [System.Windows.Forms.Clipboard]::GetImage();[System.Drawing.Bitmap]$image.Save('" filename "',[System.Drawing.Imaging.ImageFormat]::Png); Write-Output 'clipboard content saved as file'} else {Write-Output 'clipboard does not contain image data'}\""))
   (insert (concat "[[file:" filename "]]"))
   (org-display-inline-images))

The whole write-up can be found here

查看更多
我欲成王,谁敢阻挡
3楼-- · 2020-05-13 20:51

The answers here focus on initiating the process within emacs. An alternative, on macos, is to place a screenshot image on the system clipboard using cmd-ctl-shift-4, and then to return to Emacs and use emacs lisp to call pngpaste to write the image from clipboard to file, and do whatever you need to with the image file (insert into org, LaTeX, or as a native Emacs inline image etc).

For example, to insert into org:

(defun org-insert-clipboard-image (&optional file)
  (interactive "F")
  (shell-command (concat "pngpaste " file))
  (insert (concat "[[" file "]]"))
  (org-display-inline-images))
查看更多
可以哭但决不认输i
4楼-- · 2020-05-13 20:53

The org-download package is designed for this.

查看更多
放荡不羁爱自由
5楼-- · 2020-05-13 20:54

For OS X users. The following compliments Assem's answer.

The import command was not working for me, so I swapped it to screencapture with an -i argument. I also find it more convenient to use a relative path for the link rather than the absolute.

(defun my-org-screenshot ()
  "Take a screenshot into a time stamped unique-named file in the
same directory as the org-buffer and insert a link to this file."
  (interactive)
  (setq filename
        (concat
         (make-temp-name
          (concat (file-name-nondirectory (buffer-file-name))
                  "_"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (call-process "screencapture" nil nil nil "-i" filename)
  (insert (concat "[[./" filename "]]"))
  (org-display-inline-images))

Update: Improvements

I have recently improved on the script a little. It now places screenshots in a subdirectory, creates the directory if required, and only inserts the link into emacs if the image was actually created (i.e., does nothing if you hit ESC). It also works on both Ubuntu and OS X.

(defun my-org-screenshot ()
  "Take a screenshot into a time stamped unique-named file in the
same directory as the org-buffer and insert a link to this file."
  (interactive)
  (org-display-inline-images)
  (setq filename
        (concat
         (make-temp-name
          (concat (file-name-nondirectory (buffer-file-name))
                  "_imgs/"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (unless (file-exists-p (file-name-directory filename))
    (make-directory (file-name-directory filename)))
  ; take screenshot
  (if (eq system-type 'darwin)
      (call-process "screencapture" nil nil nil "-i" filename))
  (if (eq system-type 'gnu/linux)
      (call-process "import" nil nil nil filename))
  ; insert into file if correctly taken
  (if (file-exists-p filename)
    (insert (concat "[[file:" filename "]]"))))
查看更多
做自己的国王
6楼-- · 2020-05-13 21:00

The exact functionality you want isn't currently implemented, but I would be skeptical of saving lots of images into a research log if your opinion is that you "definitely don't want to save them."

Anyways, the functionality you desire has been expressed in the org-mode mailing list a couple of times in recent years - check

http://comments.gmane.org/gmane.emacs.orgmode/33770

http://www.mail-archive.com/emacs-orgmode@gnu.org/msg50862.html

The first link includes some code to launch a screenshot utility (via ImageMagick) to [uniquely] save the file and insert an inline link in your org-mode buffer.

As stated in that thread, the code was improved upon and added to org-mode hacks page - which has lots of useful gems:

http://orgmode.org/worg/org-hacks.html

(defun my-org-screenshot ()
  "Take a screenshot into a time stamped unique-named file in the
same directory as the org-buffer and insert a link to this file."
  (interactive)
  (setq filename
        (concat
         (make-temp-name
          (concat (buffer-file-name)
                  "_"
                  (format-time-string "%Y%m%d_%H%M%S_")) ) ".png"))
  (call-process "import" nil nil nil filename)
  (insert (concat "[[" filename "]]"))
  (org-display-inline-images))
查看更多
太酷不给撩
7楼-- · 2020-05-13 21:04

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.

查看更多
登录 后发表回答