How to render PDF using pdf.js viewer in PyQt?
Asked Answered
S

3

6

I have tried adding the pdf.js viewer files in my project and it works in browsers like Chrome, Mozilla, Safari, etc, but it's not loading some pages in node-webkit and PyQt webkit.

I am trying to load the file using an iframe, like this:

<iframe src="/test/?file=/assets/pdf/example.pdf#page=3"> </iframe>
Sufficiency answered 30/4, 2014 at 13:8 Comment(2)
Showing your relevant code is generally strongly preferred.Calefactory
crosspost github.com/mozilla/pdf.js/issues/4715Attic
B
7

I've found this thread over at the Qt Forums, where thebeast44 posted a snippet of Qt code answering your question. My translation to python is below.

You'll also need to unpack the res folder from the author's original code, I think he just modified the viewer... I've also attached said code 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_())
Bloodless answered 5/6, 2014 at 3:43 Comment(1)
Thank you So much @Bloodless . i works in node-webkit also in qtpy thanks you so much .Sufficiency
C
9

Below are some more up-to-date demo scripts for using pdf.js with PyQt4/QtWebKit or PyQt5/QtWebEngine. To try these, first download the latest stable version of pdf.js and unpack the zip file into a suitable location. (NB: if you're on Linux your distro may already have a pdf.js package, so that could be installed instead).

UPDATE:

As of Qt-5.13.0, it is also possible to use the built-in Chromium PDF Viewer with QWebEngineView:

screenshot

import sys
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets

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

class Window(QtWebEngineWidgets.QWebEngineView):
    def __init__(self):
        super().__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 script:

UPDATE:

NB: as of Aug 2022, it may be necessary to use the legacy build of pdfjs (i.e. the build for "older browsers" on the download page) to keep things working with PyQt5. The stable build should work okay with PyQt6, though.

screenshot

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().__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 script:

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().__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_())
Crown answered 1/1, 2018 at 20:30 Comment(9)
When I run the PyQt5/QtWebEngine script, I see "0 of 0" and "automatic zoom" in the menu bar but the display area is empty. The following error is displayed in the console: js: Uncaught (in promise) ReferenceError: globalThis is not defined. I am running the most recent anaconda packages (python 3.8, qt 5.9.7, pyqt 5.9.2) on Ubuntu 20.04. Any ideas?Tocantins
@Tocantins I just tested the script in my answer using pdfjs-2.6.347 with qt 5.15.2 and pyqt 5.15.2 and it works fine for me on linux. Does ubuntu have a pdfjs package? If it does, you should use that, otherwise there may be version incompatibilities.Crown
Thank you for confirming. I did not realize it was necessary to install PyQtWebEngine in addition to Qt and PyQt5. Fixed with "pip install PyQtWebEngine".Tocantins
When I try the PyQt5/QtWebEngine script I get the error message js: Uncaught SyntaxError: Unexpected token . js: Uncaught SyntaxError: Unexpected token ? The viewer controls are visible but the content remains blank. I'm using python 3.7 / windows 10 / pyqtwebengine 5.12.1 / pyqt 5.12.3. Any Ideas?Aspen
@Aspen Works fine for me using qt-5.15.2 and pdfjs-2.11.388 on linux. You could also use the built-in chromium pdf viewer if you can upgrade to qt-5.13.x or greater. See my updated answer.Crown
Thank you for adding this. I'm using conda and am stuck with pyqt5<5.13 for now. (see github.com/spyder-ide/spyder/issues/12829) Independent of this I don't see why your PyQt5/QtWebEngine pdfjs script would not work in my installation.Aspen
@Aspen Well, obviously there's a version compatibility issue. If you insist on using outdated software like conda, it's inevitable this will happen sooner or later. Which version of pdfjs are you using? Have tried all four of the current prebuilds? If none of those work, you could try building one of the earlier releases. Of course, it could also be due to a bug in your version of qt/webengine, in which case I don't see any obvious solution (other than ditching conda, that is).Crown
@Aspen The latest version of qt-5.12 (LTS) is 5.12.12. Are you at least able to upgrade to that? (NB: official support for qt-5.12 ends on 5th Dec 2021, so there won't be any more patch releases).Crown
@Crown I only tried with the latest version pdfjs-2.10.377. pyqt 5.12.12 is not on conda-forge yet. I know I should change to pip and I will if there is one more issue. For now I can live without this feature and hope conda will be updated very soon. Thanks again.Aspen
B
7

I've found this thread over at the Qt Forums, where thebeast44 posted a snippet of Qt code answering your question. My translation to python is below.

You'll also need to unpack the res folder from the author's original code, I think he just modified the viewer... I've also attached said code 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_())
Bloodless answered 5/6, 2014 at 3:43 Comment(1)
Thank you So much @Bloodless . i works in node-webkit also in qtpy thanks you so much .Sufficiency
M
2

From PyQt5 v5.13 you can load PDF files with the chromium API. According to the documentation https://doc.qt.io/qt-5/qtwebengine-features.html#pdf-file-viewing this option is by default enabled.

This minimal example is adapted from 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_())

This example:

This example

Mogilev answered 5/7, 2020 at 14:44 Comment(2)
QAxContainer is Windows specific according to doc.qt.io/qt-5/qaxcontainer-module.html. Is there another option for Linux users?Tocantins
Unfortunately not :(Mogilev

© 2022 - 2024 — McMap. All rights reserved.