继 (分享)eaf-pdf-viewer 支持 PDF/EPUB 文件行搜索和实时预览
中解决 eaf-pdf-viewer 的搜索效率问题之后,我最近给 GitHub - emacs-eaf/eaf-pdf-viewer: Fastest PDF Viewer in Emacs 提交了一些增强光标交互流畅性的代码,主要就是用鼠标可以更精确地进行文本选择和复制,与 emacs 联动的一个功能体现在:双击 pdf/epub 中某个位置后会打开一个 emacs text buffer 并精确跳转到光标所对应的行,而如果是 latex 项目则是跳转到 tex 源码具体位置,二者行为上是一致的。
在开发中为了更高效调试和检查哪些函数是性能瓶颈,我把 eaf-pdf-viewer 分离出 emacs, (不涉及代码的分离,而是把其中涉及到 emacs 的函数用空函数替换掉了) 整理成一个单独的库: GitHub - metaescape/pqreader: pdf and epub reader depending on PyQt6 and PyMuPDF (eaf-pdf-viewer), 它直接是把 eaf-pdf-viewer 作为 submodule, 复用了其核心的代码,但不依赖于需要 emacs 的部分,因此完全基于 python。
目前支持的功能:
- 鼠标平滑滚动
- 文本选择,然后 C-c 复制
- 链接识别和点击跳转,然后用 C-t 跳转回上一次位置
- PGUP/up/C-u 以及 PGDN/down/C-d 上下翻页(半页)
=
放大,-
缩小, 0
适应屏幕宽度
- C-q 退出
有兴趣的开发者可以把它嵌入到已有的 python/PyQT 应用里,或者基于它继续扩展出一个功能更完善的桌面端 PDF/EPUB 阅读器,比如添加菜单栏,目录侧边栏,搜索框,更好地支持各种不同主题的背景色,
甚至包含书籍管理功能,AI 交互等等,毕竟 pymupdf 直接有一个叫 PyMuPDF4LLM 的接口,界面方面也许可以基于一个比较现代的 UI 框架比如 GitHub - zhiyiYo/PyQt-Fluent-Widgets: A fluent design widgets library based on C++ Qt/PyQt/PySide. Make Qt Great Again.
(关于 pdf 换背景的额外说明:目前 eaf-pdf-viewer 也有背景跟随 emacs 主题的功能,但在不额外引入图像处理库的情况下,手写出高效而稳健的适用于各种复杂颜色的扣背景的功能还是比较困难的,所以目前用的是很简单的方式,以至于有些背景色丰富一点的pdf 抠出来的文字会有很多锯齿,但 epub 这类偏纯文本的格式换背景效果没问题)
我个人阅读时最常用的功能,除纯浏览之外有文本复制,全文搜索,链接上下文来回跳转,用 org 快速编辑作为笔记型书签的 TOC 并用 minibuffer 搜索书签(一般不直接在 pdf 上做标注),目前 eaf-pdf-viewer 都有了并且很流畅了,估计一段时间没什么想法了,感谢 @manateelazycat 和 @MatthewZMD 合并代码以及其他开发者的贡献。
最后还剩下几个小的不影响功能但稍微影响体验的问题,在此求助:
- 每次光标从 emacs 外移动到 pdf-viewer 上,会出现一个黑色的方块(应该是 PyQT 的 Tooltips ,有时候其中会显示出 pdf 的文件名,但更多时候是全黑),按其他按键或点击鼠标之后就消失,我把 pdf-viewer 中所有涉及 Tooltips 的代码都注释了,都没法消除这个方框,感觉很是奇怪。 在 eaf 主仓源码里也没找到 tooltips 或 mouse hover 有关的代码。
- 另外,有时候光标位置的计算不太准确,尤其是刚打开一个 pdf 文件或者切换窗口后回到 emacs ,把光标移动到 viewer 的最上方接近边框的位置,它的 cursor posion 的 y 坐标应该接近 0, 但我加打印之后有时会是 20-30 (像素), 这导致光标检测到的是下一行的文本,不过当快速打开一个 minibuffer 再关闭后,光标位置又准确了,并且不再切换的话也一直是准的(怀疑是 emacs 端的窗口坐标信息没有及时传到 python 端导致的)
注:从 emacs 中分离出后的 pqreader 不会有以上提到的问题.
4 个赞
确实存在第二个问题,选中的行是偏下的行。
在另一个独立的pdf阅读器qpdfview里,使用mupdf做后端时也存在相同的问题。现在也不确定是emacs的问题还是mupdf库的问题。
我这边左右调整窗口大小会恢复正常。
你可以试试我分离出的那个 pqreader,这里面我自己测试光标是准的,目前感觉像是 emacs 和 eaf 对齐同步的问题。第一个你没有的话,我去重新把 eaf 删了再下载看看,不知道是不是什么残留的 pyc 文件导致。
— 删掉重新下载结果还是有这个问题
在 Reader.eventFilter 函数里加上
elif is_key_pressed and key == Qt.Key.Key_Escape:
self.cleanup_select()
(eaf-bind-key action_quit "<escape>" eaf-pdf-viewer-keybinding)
这样应该可以
1 个赞
第二个问题我把 eaf 主仓里 eaf.py 文件 268 到 279 行都注释了,如下,之后光标就准了,主要是打印发现切换窗口后,self.view_dict 里会有相同 buffer 对应的不同 view ,然后觉得取最大尺寸的 view 有点奇怪,就先注释看,发现确实可以。
for buffer in list(self.buffer_dict.values()):
if not buffer.fit_to_view:
# buffer_views = list(filter(lambda v: v.buffer_id == buffer.buffer_id, list(self.view_dict.values())))
# # Adjust buffer size to max view's size.
# if len(buffer_views) > 0:
# max_view = max(buffer_views, key=lambda v: v.width * v.height)
# buffer.buffer_widget.width, buffer.buffer_widget.height = lambda: max_view.width, lambda: max_view.height
# buffer.buffer_widget.resize(max_view.width, max_view.height)
# # Adjust buffer size to emacs window size if not match view found.
# else:
# buffer.buffer_widget.width, buffer.buffer_widget.height = lambda: emacs_width, lambda: emacs_height
# buffer.buffer_widget.resize(emacs_width, emacs_height)
# Send resize signal to buffer.
buffer.resize_view()
但我对与之相关的代码都不太熟悉,不知道会不会影响其他 app 使用,具体评估要看 @manateelazycat 大佬有没空了,先自己本地这么用着。
1 个赞
268 ~ 279 的代码是 EAF 最早的版本就写的代码, 那时候是强制所有App的 buffer widget resize 大小。
但是随着后面几年 JavaScript/Vue 的App开发, 每个 App 都通过继承 Buffer resize_view() 接口来独自处理App内部的resize逻辑。
所以 268 ~ 279 的代码基本上也没用了, 只是平常不用鼠标没有发现这个问题, 我一会来把这份代码删除吧。
1 个赞
我发现这个代码还不能删除, 删除后 eaf-file-manager 移动光标就挂了。
这段代码删除后, eaf-file-manager 按 j 移动光标, 就没法看到光标了, 应该是 buffer 对应的 chromium view 大小没有发生变化。
那个选相同 buffer 里尺寸最大的 view 感觉不是很合理,是否应该选最新创建的 view 去设置
这个要多测试, 因为EAF App很多。
resize_view 那里, 有些Application是重新实现了, 有些Applicaiton还是依赖 eaf.py 里面默认的resize操作。
那或许可以先判断 self.buffer_widget 里有没有 resize 这个 attr, 有的就直接 调用 self.buffer.resize_view,没有的再用原始的代码,先这么绕过去
先多测试吧, eaf.el/eaf.py 改动代码都要慎重。