如何在 PyQt 中使用 pdf.js 查看器呈现 PDF?

Posted

技术标签:

【中文标题】如何在 PyQt 中使用 pdf.js 查看器呈现 PDF?【英文标题】:How to render PDF using pdf.js viewer in PyQt? 【发布时间】:2022-01-07 02:31:22 【问题描述】:

我尝试在我的项目中添加 pdf.js 查看器文件,它可以在 Chrome、Mozilla、Safari 等浏览器中运行,但它没有在 node-webkit 和 PyQt webkit 中加载某些页面。

我正在尝试使用 iframe 加载文件,如下所示:

<iframe src="/test/?file=/assets/pdf/example.pdf#page=3"> </iframe>

【问题讨论】:

通常强烈建议显示相关代码。 crosspost github.com/mozilla/pdf.js/issues/4715 【参考方案1】:

I've found this thread over at the Qt Forums,thebeast44 在这里发布了 Qt 代码的 sn-p 来回答您的问题。我对 python 的翻译如下。

你还需要从author's original code解压res文件夹,我想他只是修改了查看器...我还附上了上述代码here。

from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4 import QtNetwork
from PyQt4 import QtWebKit


class PDFViewer(QtWebKit.QWebView):
    pdf_viewer_page = 'res/pdf-viewer.html'

    def __init__(self, parent=None):
        super().__init__(parent)
        self.settings = QtWebKit.QWebSettings.globalSettings()
        self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessFileUrls, True )
        self.settings.setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True )
        self.settings.setAttribute(QtWebKit.QWebSettings.DeveloperExtrasEnabled, True )
        nam = QtNetwork.QNetworkAccessManager()
        page = QtWebKit.QWebPage(self)
        page.setNetworkAccessManager(nam)
        self.setPage(page)
        self.loadFinished.connect(self.onLoadFinish)
        self.setUrl(QtCore.QUrl(self.pdf_viewer_page))

    def onLoadFinish(self, success):
        if success:
            self.page().mainFrame().evaluatejavascript("init();")


if __name__ == '__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    viewer = PDFViewer(parent=None)
    viewer.show()
    sys.exit(app.exec_())

【讨论】:

非常感谢@fstafforini。我在 node-webkit 中工作,也在 qtpy 中工作,非常感谢。【参考方案2】:

下面是一些最新的演示脚本,用于将pdf.js 与 PyQt4/QtWebKit 或 PyQt5/QtWebEngine 一起使用。要尝试这些,首先download the latest stable version of pdf.js 并将 zip 文件解压缩到合适的位置。 (注意:如果你在 Linux 上,你的发行版可能已经有一个 pdf.js 包,所以可以安装它)。

更新

从 Qt-5.13.0 开始,还可以将 built-in Chromium PDF Viewer 与 QWebEngineView 一起使用:

import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets

PDF = 'file://path/to/my/sample.pdf'

class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self):
        super(Window, self).__init__()
        self.settings().setAttribute(
            QtWebEngineWidgets.QWebEngineSettings.PluginsEnabled, True)
        self.settings().setAttribute(
            QtWebEngineWidgets.QWebEngineSettings.PdfViewerEnabled, True)
        self.load(QtCore.QUrl.fromUserInput(PDF))

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 800, 600)
    window.show()
    sys.exit(app.exec_())

PyQt5/QtWebEngine pdfjs 脚本:

import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets

PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'

class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self):
        super(Window, self).__init__()
        self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 800, 600)
    window.show()
    sys.exit(app.exec_())

PyQt4/QtWebKit pdfjs 脚本:

import sys
from PyQt4 import QtCore, QtGui, QtWebKit

PDFJS = 'file:///path/to/pdfjs-1.9.426-dist/web/viewer.html'
# PDFJS = 'file:///usr/share/pdf.js/web/viewer.html'
PDF = 'file:///path/to/my/sample.pdf'

class Window(QtWebKit.QWebView):
    def __init__(self):
        super(Window, self).__init__()
        self.load(QtCore.QUrl.fromUserInput('%s?file=%s' % (PDFJS, PDF)))

if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 50, 800, 600)
    window.show()
    sys.exit(app.exec_())

【讨论】:

运行 PyQt5/QtWebEngine 脚本时,我在菜单栏中看到“0 of 0”和“自动缩放”,但显示区域为空。控制台中显示以下错误:js: Uncaught (in promise) ReferenceError: globalThis is not defined。我在 Ubuntu 20.04 上运行最新的 anaconda 包(python 3.8、qt 5.9.7、pyqt 5.9.2)。有什么想法吗? @user2514157 我刚刚使用 pdfjs-2.6.347 和 qt 5.15.2 和 pyqt 5.15.2 在我的答案中测试了脚本,它在 linux 上对我来说很好用。 ubuntu 有 pdfjs 包吗?如果是这样,你应该使用它,否则可能会出现版本不兼容。 感谢您的确认。我没有意识到除了 Qt 和 PyQt5 之外还需要安装 PyQtWebEngine。使用“pip install PyQtWebEngine”修复。 当我尝试 PyQt5/QtWebEngine 脚本时,我收到错误消息 js: Uncaught SyntaxError: Unexpected token 。 js:未捕获的 SyntaxError:意外的令牌?查看器控件可见,但内容保持空白。我正在使用 python 3.7 / windows 10 / pyqtwebengine 5.12.1 / pyqt 5.12.3。有什么想法吗? @dukeeloo 在 linux 上使用 qt-5.15.2 和 pdfjs-2.11.388 对我来说效果很好。如果您可以升级到 qt-5.13.x 或更高版本,您也可以使用内置的 chromium pdf 查看器。请参阅我的更新答案。【参考方案3】:

从 PyQt5 v5.13 开始,您可以使用 chromium API 加载 PDF 文件。根据文档https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing,此选项默认启用。

这个最小的例子改编自Simple Browser

import sys
from pathlib import Path

from PyQt5 import QAxContainer
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QPushButton, QLineEdit, QApplication


class Main(QWidget):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        self.main_layout = QVBoxLayout(self)

        self.qlineedit = QLineEdit()
        self.qlineedit.returnPressed.connect(self.go_action)
        self.main_layout.addWidget(self.qlineedit)
        self.read_btn = QPushButton('Test')
        self.read_btn.clicked.connect(self.go_action)
        self.main_layout.addWidget(self.read_btn)

        self.WebBrowser = QAxContainer.QAxWidget(self)
        self.WebBrowser.setFocusPolicy(Qt.StrongFocus)
        self.WebBrowser.setControl("8856F961-340A-11D0-A96B-00C04FD705A2")
        self.main_layout.addWidget(self.WebBrowser)

    def go_action(self):
        # convert system path to web path
        f = Path(self.qlineedit.text()).as_uri()
        # load object 
        self.WebBrowser.dynamicCall('Navigate(const QString&)', f)


if __name__ == "__main__":
    a = QApplication(sys.argv)
    w = Main()
    w.show()
    sys.exit(a.exec_())

这个例子:

【讨论】:

QAxContainer 根据 doc.qt.io/qt-5/qaxcontainer-module.html 是特定于 Windows 的。 Linux 用户还有其他选择吗? 很遗憾没有:(

以上是关于如何在 PyQt 中使用 pdf.js 查看器呈现 PDF?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有 iframe 的页面中嵌入 PDF.JS 查看器

有没有办法像真正的 html 元素而不是画布或 svg 一样呈现 pdf.js 页面?

Poppler:以目标分辨率渲染

浏览器如何支持 PDF 查看以及哪些[关闭]

pdfjs获取渲染结束

Pdf.js:使用 base64 文件源而不是 url 呈现 pdf 文件