emacs 可以在 org-mode 中显示 zip 存档文件中的图像吗

Can emacs show an image in a zip archieve file in org-mode

在启用了 iimage-mode 的 emacs org-mode 中,它可以显示来自相对文件路径的内联图像,例如: [[文件:images/foo.jpg]]

我想要的是:它如何显示位于 zip 存档中的图像,如下所示: [[file:images.zip/foo.jpg]] 有办法吗?

感谢kaushalmodi, 下面是我按照这个问题 how to display pdf images in org mode 中的答案所做的。由于是第一次做lisp编程,代码可能设计的不太好

(add-hook 'org-mode-hook #'modi/org-include-img-from-zip)
(defun modi/org-include-img-from-zip (&rest ignore)
  "extract images from zip an archive.
  This function does nothing if not in org-mode."
(interactive)
(when (derived-mode-p 'org-mode)
(save-excursion
  (goto-char (point-min))
  (while (search-forward-regexp
          "^\s-*#\+STARTUP:.*\s-convertfromzip"
          nil 'noerror)
    (let* (filenoext imgext imgfile imgfilename zipfile imgpath cmd)
      (setq zipfile "arc.zip")
      ;; Keep on going on to the next line till it finds a line with
      ;; `[[FILE]]'
      (while (progn
               (forward-line 1)
               (not (looking-at "\[\[\(.*\)\.\(.*\)\]\]"))))
      (when (looking-at "\[\[file:\(.*\)\.\(.*\)\]\]")
        (setq filenoext (match-string-no-properties 1))
        (setq imgext (match-string-no-properties 2))
        (setq imgpath (concat filenoext "." imgext))
        (setq imgfile (expand-file-name (concat filenoext "." imgext)))
        (setq imgfilename (file-name-nondirectory imgfile))
        (when (string= (substring imgpath 0 1) "~")
          (setq imgpath (concat "%HOME%" (substring imgpath 1 nil)))
          (setq imgpath (file-name-directory imgpath))
            (setq cmd (concat "7z e " zipfile " -y -o"imgpath " " imgfilename " " imgfilename ))
            (with-temp-buffer (shell-command cmd t))
          )
        ))))))

工作规则:

  • 如果您有包含图像 myimage.png 的 zip 文件 zippedimg.zip,您应该在组织模式文件中将其引用为 [[./zippedimg_zip/myimage.png]]。请注意,我已将 zippedimg.zip 中的 . 替换为 _
  • 这样做的原因是我正在创建与 [[]] 中提到的目录相同的目录。但是由于 zippedimg.zip 文件存在,emacs 不允许创建同名目录。因此,对于 zippedimg.zip 文件,我只提取其中的图像 ,同时将 zip 中的路径保留到 zippedimg_zip 目录中。

这是一个用于测试的最小工作组织文件:

#+TITLE: Image from archive
#+STARTUP: inlineimages

#+NAME: fig:myimage
#+HEADER: :extractfromarchive t
# The below caption line is optional
#+CAPTION: My image myimage.png inside ./zippedimg.zip
[[./zippedimg_zip/myimage.png]]

这是您需要放在 emacs init 某处的代码:

;; Execute the `modi/org-include-img-from-archive' function just before saving the file
(add-hook 'before-save-hook #'modi/org-include-img-from-archive)
;; Execute the `modi/org-include-img-from-archive' function before processing the
;; file for export
(add-hook 'org-export-before-processing-hook #'modi/org-include-img-from-archive)

(defun modi/org-include-img-from-archive (&rest ignore)
  "Extract image files from the archive files. Only .zip files are supported
as of now.

Only looks at #HEADER: lines that have \":extractfromarchive t\".
This function does nothing if not in org-mode, so you can safely
add it to `before-save-hook'."
  (interactive)
  (when (derived-mode-p 'org-mode)
    (save-excursion
      (goto-char (point-min)) ; go to the beginning of the buffer
      (while (search-forward-regexp
              "^\s-*#\+HEADER:.*\s-:extractfromarchive\s-+t"
              nil :noerror)
        (let (;; only .zip supported as of now
              (search-expr "\[\[\(.*?\)_zip/\(.*?\)\([^/]+\..*\)\]\]")
              arc-file
              path-in-arc-file
              img-file img-file-full-path
              dest-dir dest-dir-full-path
              cmd)
          ;; Keep on going on to the next line till it finds a line with
          ;; `[[./path/to/zip-file/path/inside/zip/to/the/image]]'
          (while (progn
                   (forward-line 1)
                   (or (not (looking-at search-expr))
                       (eobp))))
          (when (looking-at search-expr)
            (setq arc-file (expand-file-name
                            (concat (match-string-no-properties 1) ".zip")))
            (setq path-in-arc-file (match-string-no-properties 2))
            (setq img-file (match-string-no-properties 3))
            (setq dest-dir (concat "./" (file-name-base arc-file)
                                   "_zip/" path-in-arc-file))
            (setq dest-dir-full-path (concat (file-name-sans-extension arc-file)
                                             "_zip/" path-in-arc-file))
            (setq img-file-full-path (expand-file-name img-file dest-dir))
            ;; (message (concat "arc-file: %s\npath-in-arc-file: %s\n"
            ;;                  "img-file: %s\nimg-file-full-path: %s\n"
            ;;                  "dest-dir: %s\ndest-dir-full-path: %s")
            ;;          arc-file path-in-arc-file
            ;;          img-file img-file-full-path
            ;;          dest-dir dest-dir-full-path)

            (when (file-newer-than-file-p arc-file img-file-full-path)
              ;; This block is executed only if arc-file is newer than
              ;; img-file-full-path
              ;; or if img-file does not exist
              ;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Testing-Accessibility.html
              (when (file-exists-p dest-dir-full-path)
                (delete-directory dest-dir-full-path t t))
              (make-directory dest-dir-full-path t)
              (setq cmd (format "unzip -j %s %s%s -d ./%s."
                                arc-file path-in-arc-file img-file
                                (concat (file-name-base arc-file) "_zip/"
                                        path-in-arc-file)))
              (message "%s" cmd)
              (shell-command cmd)
              (shell-command (concat "touch " img-file-full-path)))))))))