Emacs中用zotero(优先)或evince(次之)打开pdf文件(open-pdf-in-zotero-or-evince)

我用chatgpt写了一段代码,用zotero打开相应的pdf文件。

原理是提取pdf最后两级路径,之后调用终端异步执行 open zotero://open-pdf/library/items/…(item-name/pdf-name)

因为zotero基本上是最好用的pdf软件,做笔记,与emacs联动都很方便。

目前缺点是,用zotero打开的同时,emacs中会用pdf-viewer-mode再打开一次pdf,这个缺点我目前无法解决,如果有兴趣,请帮忙解决,我相信这会很有用!

如果能把它做成一个minor mode,就更好了!

代码如下:

(defun open-pdf-in-zotero ()
  "Open the current PDF file in Zotero."
  (interactive)
(let* ((pdf-path buffer-file-name)
       (pdf-name (file-name-nondirectory pdf-path))
       (pdf-dir (file-name-directory pdf-path))
       (parent-dir (file-name-nondirectory (directory-file-name pdf-dir)))
       (grandparent-dir (file-name-nondirectory (directory-file-name (file-name-directory pdf-dir))))
       (zotero-path (concat "zotero://open-pdf/library/items/" grandparent-dir "/" parent-dir "/" pdf-name)))
  (async-start-process "open-zotero" "open" nil zotero-path)))

(add-hook 'pdf-view-mode-hook 'open-pdf-in-zotero)

谢谢!

1 个赞

另外,这个函数可以用zotero打开orb-note-actions和helm-bibtex中的链接。

其实只要zotero里面有这个条目,都可以用这个函数通过zotero打开,因为这个函数是和pdf-viewer-mode挂钩的。希望能实现以下功能:

当zotero库中有这个条目时,只用zotero打开,不用pdf-iewer-mode打开。

当zotero库中没有这个条目时,用openwith-mode打开。(openwith在windows上可以设置成sumatra,在linux上可以设置成evince)

并且把这个mode设置成zotero-mode,如上所述,它是先于openwith-mode的。

进阶:制作一个在org中的页数标签,可以与orb的链接一起插入文本中。点击这个链接,就自动跳转到zotero的那一个文档的那一页。

关于为什么不用org-noter:因为太老旧了,pdf滚动太慢。不能直接和orb联动。并且不能直接创建一个文献笔记页面。

关于为什么不用eaf-pdf-viewer:尝试了几天用linux安装,安装之后不能运行。这应该是我的技术问题,不过这也反映出,安装对新手来说是复杂的,各平台安装方法也不一样,有些难度,各平台和各桌面环境支持程度也不一。另外,zotero功能可能要多点。

这个zotero函数像是低配版的org-noter加上eaf-pdf-viewer。希望能够体谅emacs新手的不足!

1 个赞

eaf安装不能运行, 请看中文README底部, 写的有解决方案, 一般都是Python没有安装好。

你可能应该直接改orb-note-actions和helm-bibtex打开链接的方式,而不是打开 PDF 之后再 hook

1 个赞
    (defun private/open-in-zotero (citekey)
      "Open a reference item in Zotero."
      (interactive (list (citar-select-ref)))
      (citar-file-open-external
       (concat "zotero://select/items/@" citekey)))

可以通过 citekey 来打开文件,这里的 citar-file-open-external 不重要,只要有 citekey 了,拼出链接,后面总能打开文件。

2 个赞

谢谢你!不过我现在还不用citar,我目前在用org-ref,另外我想直接在pdf链接上操作,用zotero(优先)或者evince(次之)方式打开。

谢谢!我去看看

谢谢建议,我刚刚用chatgpt生成了段代码避免了这个问题,我等下附上。

下面这段代码解决了以上问题。

功能是:判断这个文件在不在zotero库里。如果在,则用zotero打开。如果不在,则用evince打开。emacs里面不会再用pdf-viewer-mode打开了。

直接粘在init文件里应该就可以用。这将替换emacs里所有pdf打开的方式。

evince也可以按需要替换成其他的软件。

(defun expand-tilde (path)
  "Expand tilde in PATH to user's home directory."
  (if (string-prefix-p "~" path)
     (concat (getenv "HOME") (substring path 1))
   path))

(defun open-pdf-in-zotero-or-evince (pdf-path)
  "Open the PDF file at PDF-PATH in Zotero if it is in a Zotero storage directory, otherwise use Evince."
  (setq pdf-path (expand-tilde pdf-path))
  (if (string-match "/storage/" pdf-path)
     (let* ((pdf-name (file-name-nondirectory pdf-path))
           (pdf-dir (file-name-directory pdf-path))
           (parent-dir (file-name-nondirectory (directory-file-name pdf-dir)))
           (grandparent-dir (file-name-nondirectory (directory-file-name (file-name-directory pdf-dir))))
           (zotero-path (concat "zotero://open-pdf/library/items/" grandparent-dir "/" parent-dir "/" pdf-name)))
       (async-start-process "open-zotero" "open" nil zotero-path))
   (async-start-process "open-evince" "evince" nil pdf-path)))

(defun my-find-file-advice (orig-fun &rest args)
  "Advice function around ORIG-FUN to open PDFs in Zotero or Evince."
  (let ((file (car args)))
   (if (string-equal (file-name-extension file) "pdf")
      (open-pdf-in-zotero-or-evince file)
    (apply orig-fun args))))

(advice-add 'find-file :around #'my-find-file-advice)
1 个赞

以下是windows系统里的代码:

(defun expand-tilde (path)
  "Expand tilde in PATH to user's home directory."
  (if (string-prefix-p "~" path)
      (concat (getenv "HOME") (substring path 1))
    path))

(defun open-pdf-in-zotero-or-reader (pdf-path)
  "Open the PDF file at PDF-PATH in Zotero if it is in a Zotero storage directory, otherwise use the default PDF reader."
  (setq pdf-path (expand-tilde pdf-path))
  (let ((zotero-path "D:/zotero_7/zotero.exe")
        (zotero-uri nil))
    (if (string-match "/storage/" pdf-path)
        (let* ((pdf-name (file-name-nondirectory pdf-path))
               (pdf-dir (file-name-directory pdf-path))
               (parent-dir (file-name-nondirectory (directory-file-name pdf-dir)))
               (grandparent-dir (file-name-nondirectory (directory-file-name (file-name-directory pdf-dir)))))
          (setq zotero-uri (format "zotero://open-pdf/library/items/%s/%s/%s" grandparent-dir parent-dir pdf-name)))
      (setq zotero-uri (format "zotero://open-pdf/library/items/%s" (file-name-sans-extension (file-name-nondirectory pdf-path)))))
    (if (and (file-exists-p zotero-path) zotero-uri)
        (progn
          (start-process "zotero" nil zotero-path "-url" zotero-uri)
          (message "Attempting to open PDF in Zotero..."))
      (start-process "open-pdf" nil "explorer" pdf-path))))

(defun my-find-file-advice (orig-fun &rest args)
  "Advice function around ORIG-FUN to open PDFs in Zotero or default PDF reader."
  (let ((file (car args)))
    (if (string-equal (file-name-extension file) "pdf")
        (open-pdf-in-zotero-or-reader file)
      (apply orig-fun args))))

(advice-add 'find-file :around #'my-find-file-advice)

1 个赞