粘贴剪切板中的图片为 Markdown 链接(概念验证)


#1

我们的论坛(Discourse)、GitHub、Pages.app 和一些 Markdown 编辑器都有这个功能。

所幸 Emacs Mac Port 支持剪切板中图片格式(不清楚其它的 Emacs),下面利用它实现同样的功能,因为图片数据常常是 TIFF 格式的,所以我用 ImageMagick 转换成 PNG。

(defun insert-image-from-clipboard ()
  "保存剪切板图片为 clipboard.png,插入 Markdown 图片链接."
  (interactive)
  (if-let* ((s
             ;; `gui-selection-value' is not reliable
             (gui--selection-value-internal 'CLIPBOARD))
            (prop (get-text-property 0 'display s))
            (data (pcase prop (`(image . ,plist) (plist-get plist :data))))
            (file "clipboard.png"))
      (progn
        (let ((coding-system-for-write 'binary))
          (write-region data nil file))
        ;; Convert TIFF into PNG
        (shell-command (format "convert %s %s" file file))
        (cond ((derived-mode-p 'markdown-mode)
               (insert (format "![](%s)" file)))
              ((derived-mode-p 'org-mode)
               (insert (format "file:%s" file)))
              (t (insert file))))
    (user-error "No image in clipboard")))

#2

Windows上利用imagemagick也可以完美支持剪贴板图片。


#3

不知道 ImageMagick 能读取剪切板,貌似只支持 Windows,在 macOS 用以下命令试了下:

~ $ magick clipboard: test.png
magick: unable to open image 'clipboard:': No such file or directory @ error/blob.c/OpenBlob/3485.
magick: no decode delegate for this image format `' @ error/constitute.c/ReadImage/556.
~ $

用命令行工具读取剪切板应该是最 Portable 的方案了,我怀疑除了 Emacs Mac Port,其它 Emacs 都不支持读取剪切板中的图片,Eli Zaretskii 说过:

Emacs doesn’t yet support non-text content of the clipboard, certainly not on MS-Windows. Volunteers are very welcome to work on that.

https://www.reddit.com/r/emacs/comments/8wikaj/paste_image_from_clipboard_on_both_mac_pc/e1w23rc/


#4

分享一下我在windows下的的粘贴函数,需要安装imagemagick。

  1. 使用任何应用软件截屏或者复制图片到剪贴板,在org-mode里粘贴即可。图片会自动保存到当前文件目录下的images文件夹里。
  2. 该函数支持粘贴剪贴板的文字和图片,所以你可以将Ctrl-y绑定到该函数上,然后如粘贴文字一样粘贴图片,当然文字也OK。
  3. Ctrl-u Ctrl-y会提示为图片命名,并在文档中引用该图片,同时会提示为图片插入caption。
  4. 在org capture buffer中粘贴时,该函数会找到目标文件的目录,并将图片保存到该目录中。
(defun org-insert-clipboard (&optional captionp)
  (interactive "P")
  (let* ((image-dir
	  (if (not (buffer-file-name))
	      (cond ((string-prefix-p "CAPTURE-[0-9]" (buffer-name))
		     (let ((buffer-name (replace-regexp-in-string "CAPTURE-[0-9-]*" "" (buffer-name))))
		       (concat (file-name-directory (buffer-file-name (get-file-buffer buffer-name))) "images")))
		    (t (yank) (error "")))
	    "images"))
	 (fname (concat (make-temp-name "image-") (format-time-string "%Y%m%d-%H%M%S")))
	 (image-file (concat image-dir "/" fname ".png"))
	 (exit-status
	  (call-process "convert" nil nil nil
			"clipboard:" image-file)))
    (if (zerop exit-status)
	(progn
	  (unless (file-exists-p image-dir) (make-directory image-dir))
	  (if captionp
	      (let ((rename (read-string "Filename to rename the temp images: ")))
		(rename-file image-file (concat image-dir "/" rename ".png") t)
		(insert (format "#+CAPTION: %s label:fig:%s\n" (read-string "Caption: ") rename))
		(kill-new (format "Fig. ref:fig:%s " rename)))
	    (insert (format "[[file:%s]]" image-file))
	    (org-display-inline-images)))
      (when captionp (user-error "No images in clipboard."))
      (yank))))