【问题标题】:Paste an image on clipboard to Emacs Org mode file without saving it将剪贴板上的图像粘贴到 Emacs Org 模式文件而不保存它
【发布时间】:2013-06-30 10:26:22
【问题描述】:

由于我使用 Emacs Org 模式作为研究日志,有时我想通过屏幕截图来跟踪某些内容,但我绝对不想保存它们。所以我想知道有没有办法将这些数字插入到我的组织模式文件中,比如从剪贴板中处理它们的单词?

【问题讨论】:

    标签: emacs screenshot org-mode


    【解决方案1】:

    您想要的确切功能目前尚未实现,但如果您认为“绝对不想保存它们”,我会怀疑将大量图像保存到研究日志中。

    不管怎样,近年来,您想要的功能已经在 org-mode 邮件列表中表达了几次 - 检查

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

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

    第一个链接包含一些代码,用于启动屏幕截图实用程序(通过 ImageMagick)以 [唯一] 保存文件并在 org-mode 缓冲区中插入内联链接。

    正如该线程中所述,代码已得到改进并添加到 org-mode hacks 页面 - 该页面有很多有用的宝石:

    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))
    

    【讨论】:

    • 谢谢,效果很好。除了我仍在努力在 Mac 上的 Aquamacs 中使用“org-display-inline-images”。另外我想知道,由于据说这种方法仅适用于基于 Unix 的系统,那么您可能知道在 Windows 上是否有任何巧妙的替代方法?
    • @lina 这个 sn-p 在 cygwin 下使用 ImageMagick 为我工作。 ImageMagick 也有一个 Windows 二进制版本,但我承认我没有尝试过。您可以在对call-process 的调用中将import 替换为任何可以指定输出文件名的快照实用程序。
    • @assem 当你的研究笔记有很多流失时,我看到了将图像嵌入到文档中作为 lina 点的好处。如果您删除包含该文件的注释,该图像将保留在您的文件系统中。您是否认为有任何方法可以修改用于粘贴的脚本(例如原始 PNG)并让 emacs 显示图像?
    • @ancechu 包含大量图像数据的缓冲区会对您的标准编辑功能产生性能影响——即使它只是跳过图像数据区域。创建一个钩子可能会更容易,该钩子会在您删除笔记时询问您是否要删除磁盘上的图像文件。
    • 在尝试运行调用过程“导入”时出现错误,“正在搜索程序:没有这样的文件或目录,导入”。它适用于我的工作机器,但不适用于家里。有什么想法吗?
    【解决方案2】:

    适用于 OS X 用户。以下是对 Assem 的 answer 的赞美。

    import 命令对我不起作用,因此我将其换成 screencapture 并带有 -i 参数。我还发现使用相对路径而不是绝对路径更方便。

    (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))
    

    更新:改进

    我最近对脚本进行了一些改进。它现在将屏幕截图放在子目录中,如果需要创建目录,并且仅在实际创建图像时将链接插入 emacs(即,如果您按 ESC,则不执行任何操作)。它也适用于 Ubuntu 和 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 "]]"))))
    

    【讨论】:

    • 如何使用这种方法截取其他屏幕的截图?如果我在调用 my-org-screenshot 时打开了 emacs,我必须截取打开的内容,即 emacs 本身。或者,我必须首先记住将 emacs 移走。如果我在使用另一个我想截屏的工具,首先我必须将 emacs 聚焦,将其移开,然后才调用my-org-screenshot。感觉很麻烦。
    • 这解决了这个问题:stackoverflow.com/a/46268816/362298
    【解决方案3】:

    这里的答案集中在启动 emacs 中的过程。在 macos 上,另一种方法是使用 cmd-ctl-shift-4 将屏幕截图图像放在系统剪贴板上,然后返回 Emacs 并使用 emacs lisp 调用 pngpaste 将图像从剪贴板写入文件,然后对图像文件做任何你需要的事情(插入到 org、LaTeX 或作为原生 Emacs 内联图像等)。

    例如,插入到组织中:

    (defun org-insert-clipboard-image (&optional file)
      (interactive "F")
      (shell-command (concat "pngpaste " file))
      (insert (concat "[[" file "]]"))
      (org-display-inline-images))
    

    【讨论】:

    【解决方案4】:

    (如果使用),我使用了@assem 的代码(谢谢 v.much btw)并创建了一个版本,它创建了一个子文件夹(如果它不存在),例如:${filename}IMG/ 然后将 img_date.png 之类的图像放入该文件夹 然后在缓冲区中插入一个相对路径,例如:
    [[ ./${filename}IMG/img_2015...png ]]

    代码如下:

    (defun my-org-screenshot ()
    "Take a screenshot into a time stamped unique-named file in the
    sub-directory (%filenameIMG) as the org-buffer and insert a link to this file."
    (interactive)
    
    (setq foldername (concat (buffer-file-name) "IMG/"))
    (if (not (file-exists-p foldername))
      (mkdir foldername))
    
    (setq imgName (concat "img_" (format-time-string "%Y_%m_%d__%H_%M_%S") ".png"))
    (setq imgPath (concat (buffer-file-name) "IMG/" imgName))
    
    (call-process "import" nil nil nil imgPath)
    
    (setq relativeFilename (concat "./"
        (buffer-name) "IMG/" imgName))
    
    (insert (concat "[[" relativeFilename "]]"))
    (org-display-inline-images)
    )
    

    [编辑 2015.04.09]

    与此同时,我发现上述方法效果不佳。从技术上讲,它可以工作,但它会生成许多与文件名匹配的文件夹名称,因为存在名称相似的文件夹,因此选择文件很繁琐。

    相反,我选择了一个解决方案,将所有 org 文件保存在同一个文件夹中,并有一个包含所有图像的“img”子文件夹。

    我使用这样的代码来捕获图像:

    (defun my/img-maker ()
     "Make folder if not exist, define image name based on time/date" 
      (setq myvar/img-folder-path (concat default-directory "img/"))
    
      ; Make img folder if it doesn't exist.
      (if (not (file-exists-p myvar/img-folder-path)) ;[ ] refactor thir and screenshot code.
           (mkdir myvar/img-folder-path))
    
      (setq myvar/img-name (concat "img_" (format-time-string "%Y_%m_%d__%H_%M_%S") ".png"))
      (setq myvar/img-Abs-Path (concat myvar/img-folder-path myvar/img-name)) ;Relative to workspace.
    
      (setq myvar/relative-filename (concat "./img/" myvar/img-name))
      (insert "[[" myvar/relative-filename "]]" "\n")
    )
    
    (defun my/org-screenshot ()
      "Take a screenshot into a time stamped unique-named file in the
     sub-directory (%filenameIMG) as the org-buffer and insert a link to this file."
      (interactive)
      (my/img-maker)
      ;(make-frame-invisible)
      (lower-frame)
      (call-process "import" nil nil nil myvar/img-Abs-Path)
    
      (raise-frame)
      ;(make-frame-visible)
      (org-display-inline-images)
    )
    

    和我的 org/img 文件夹中的一个小 bash 脚本来存档未引用的图像:

    #!/bin/sh 
    
    cd ~/git/LeoUfimtsev.github.io/org/img/
    mkdir ~/git/LeoUfimtsev.github.io/org/img/archive
    FILES=~/git/LeoUfimtsev.github.io/org/img/*.png
    for f in $FILES
    do
      var_grep_out=$(grep --directories=skip $(basename $f) ../*)
      if [ -z "$var_grep_out" ];
      then 
         echo "Archiving: $f"
         mv $f archive/
      fi
    done
    

    【讨论】:

      【解决方案5】:

      我喜欢与 org 文件一起属于的图像和其他材料位于明确关联的目录中。我为此使用了 org-attachment 机制,并在几年前写了一个包org-attachment-screenshot,它也可以从 MELPA 获得。它允许为附件提供定义目录名称(例如文档名称+一些后缀)的功能,并且还将支持标准附件功能,例如如果要基于条目定义附件并在条目内继承。 还为要执行的快照命令提供可自定义的变量。 请看一下。

      【讨论】:

        【解决方案6】:

        我的解决方案是针对 Windows 平台的。非常感谢 Assem 提供了我所依据的解决方案。

        我编写了一个 C# 控制台应用程序 CbImage2File,它将剪贴板上的图像(如果有的话)写入作为命令行参数给出的路径。您将需要对 System.Windows.Forms 程序集的引用。

        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);
                }
            }
        }
        

        我使用 Greenshot 截取屏幕截图,它(通过其配置)自动将屏幕截图复制到剪贴板。

        然后我使用快捷方式C-S-v 使用函数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)
        

        这个函数会计算出一个文件路径,然后调用C#应用程序创建png文件,然后它会添加图像标签,然后调用org-display-inline-images来显示图像。如果剪贴板上没有图像,它将粘贴到来自 C# 应用程序的响应中。

        【讨论】:

          【解决方案7】:

          org-download 包就是为此而设计的。

          【讨论】:

          【解决方案8】:

          对于 Windows 10 用户,我修改了 @assem 提供的答案以使用开箱即用的工具:

          (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))
          

          完整的文章可以在here找到

          【讨论】:

          • 太好了,谢谢。我必须在调用 snippingtool 和 powershell 脚本之间添加一个 (sleep-for 0.5),否则 ContainsImage() 调用将返回 false。但这很棒。
          • 优秀。一个很好的补充将是控制是否首先调用截图工具的参数。有时我剪贴板上已经有一张图片,不想调用截图工具。
          猜你喜欢
          • 1970-01-01
          • 2012-04-16
          • 2013-07-08
          • 1970-01-01
          • 2023-04-08
          • 2013-12-06
          • 1970-01-01
          • 2012-05-02
          • 2018-06-14
          相关资源
          最近更新 更多