Initial attempt at getting PDF.js to work with separate downloads

This commit is contained in:
Florian Bruhin 2018-09-06 00:11:28 +02:00
parent 8f1690eff7
commit df67c254f8
4 changed files with 75 additions and 14 deletions

View File

@ -32,10 +32,11 @@ import enum
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex, from PyQt5.QtCore import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
QTimer, QAbstractListModel, QUrl) QTimer, QAbstractListModel, QUrl)
from qutebrowser.browser import pdfjs
from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.utils import (usertypes, standarddir, utils, message, log, from qutebrowser.utils import (usertypes, standarddir, utils, message, log,
qtutils) qtutils, objreg)
from qutebrowser.qt import sip from qutebrowser.qt import sip
@ -224,9 +225,6 @@ class _DownloadTarget:
"""Abstract base class for different download targets.""" """Abstract base class for different download targets."""
def __init__(self):
raise NotImplementedError
def suggested_filename(self): def suggested_filename(self):
"""Get the suggested filename for this download target.""" """Get the suggested filename for this download target."""
raise NotImplementedError raise NotImplementedError
@ -300,6 +298,17 @@ class OpenFileDownloadTarget(_DownloadTarget):
return 'temporary file' return 'temporary file'
class PDFJSDownloadTarget(_DownloadTarget):
"""Open the download via PDF.js."""
def suggested_filename(self):
raise NoFilenameError
def __str__(self):
return 'temporary PDF.js file'
class DownloadItemStats(QObject): class DownloadItemStats(QObject):
"""Statistics (bytes done, total bytes, time, etc.) about a download. """Statistics (bytes done, total bytes, time, etc.) about a download.
@ -405,6 +414,8 @@ class AbstractDownloadItem(QObject):
arg: The error message as string. arg: The error message as string.
remove_requested: Emitted when the removal of this download was remove_requested: Emitted when the removal of this download was
requested. requested.
pdfjs_requested: Emitted when PDF.js should be opened with the given
filename.
""" """
data_changed = pyqtSignal() data_changed = pyqtSignal()
@ -412,6 +423,7 @@ class AbstractDownloadItem(QObject):
error = pyqtSignal(str) error = pyqtSignal(str)
cancelled = pyqtSignal() cancelled = pyqtSignal()
remove_requested = pyqtSignal() remove_requested = pyqtSignal()
pdfjs_requested = pyqtSignal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
@ -730,6 +742,19 @@ class AbstractDownloadItem(QObject):
return return
self.open_file(cmdline) self.open_file(cmdline)
def _pdfjs_if_successful(self):
"""Open the file via PDF.js if downloading was successful."""
if not self.successful:
log.downloads.debug("{} finished but not successful, not opening!"
.format(self))
return
filename = self._get_open_filename()
if filename is None: # pragma: no cover
log.downloads.error("No filename to open the download!")
return
self.pdfjs_requested.emit(filename)
def set_target(self, target): def set_target(self, target):
"""Set the target for a given download. """Set the target for a given download.
@ -741,7 +766,7 @@ class AbstractDownloadItem(QObject):
elif isinstance(target, FileDownloadTarget): elif isinstance(target, FileDownloadTarget):
self._set_filename( self._set_filename(
target.filename, force_overwrite=target.force_overwrite) target.filename, force_overwrite=target.force_overwrite)
elif isinstance(target, OpenFileDownloadTarget): elif isinstance(target, (OpenFileDownloadTarget, PDFJSDownloadTarget)):
try: try:
fobj = temp_download_manager.get_tmpfile(self.basename) fobj = temp_download_manager.get_tmpfile(self.basename)
except OSError as exc: except OSError as exc:
@ -749,8 +774,15 @@ class AbstractDownloadItem(QObject):
message.error(msg) message.error(msg)
self.cancel() self.cancel()
return return
self.finished.connect(
functools.partial(self._open_if_successful, target.cmdline)) if isinstance(target, OpenFileDownloadTarget):
self.finished.connect(
functools.partial(self._open_if_successful, target.cmdline))
elif isinstance(target, PDFJSDownloadTarget):
self.finished.connect(self._pdfjs_if_successful)
else:
assert False, target
self._set_tempfile(fobj) self._set_tempfile(fobj)
else: # pragma: no cover else: # pragma: no cover
raise ValueError("Unsupported download target: {}".format(target)) raise ValueError("Unsupported download target: {}".format(target))
@ -797,6 +829,13 @@ class AbstractDownloadManager(QObject):
dl.stats.update_speed() dl.stats.update_speed()
self.data_changed.emit(-1) self.data_changed.emit(-1)
@pyqtSlot(str)
def _on_pdfjs_requested(self, filename):
"""Open PDF.js when a download requests it."""
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused')
tabbed_browser.tabopen(pdfjs.get_main_url(filename))
def _init_item(self, download, auto_remove, suggested_filename): def _init_item(self, download, auto_remove, suggested_filename):
"""Initialize a newly created DownloadItem.""" """Initialize a newly created DownloadItem."""
download.cancelled.connect(download.remove) download.cancelled.connect(download.remove)
@ -813,6 +852,8 @@ class AbstractDownloadManager(QObject):
download.data_changed.connect( download.data_changed.connect(
functools.partial(self._on_data_changed, download)) functools.partial(self._on_data_changed, download))
download.error.connect(self._on_error) download.error.connect(self._on_error)
download.pdfjs_requested.connect(self._on_pdfjs_requested)
download.basename = suggested_filename download.basename = suggested_filename
idx = len(self.downloads) idx = len(self.downloads)
download.index = idx + 1 # "Human readable" index download.index = idx + 1 # "Human readable" index

View File

@ -22,9 +22,10 @@
import os import os
from PyQt5.QtCore import QUrl from PyQt5.QtCore import QUrl, QUrlQuery
from qutebrowser.utils import utils, javascript, jinja from qutebrowser.utils import utils, javascript, jinja
from qutebrowser.config import config
class PDFJSNotFound(Exception): class PDFJSNotFound(Exception):
@ -210,3 +211,17 @@ def is_available():
return False return False
else: else:
return True return True
def should_use_pdfjs(mimetype):
return (mimetype in ['application/pdf', 'application/x-pdf'] and
config.val.content.pdfjs)
def get_main_url(filename):
"""Get the URL to be opened to view a local PDF."""
url = QUrl('qute://pdfjs')
query = QUrlQuery()
query.addQueryItem('file', QUrl.fromLocalFile(filename).toString())
url.setQuery(query)
return url

View File

@ -523,6 +523,11 @@ def qute_pastebin_version(_url):
@add_handler('pdfjs') @add_handler('pdfjs')
def qute_pdfjs(url): def qute_pdfjs(url):
"""Handler for qute://pdfjs. Return the pdf.js viewer.""" """Handler for qute://pdfjs. Return the pdf.js viewer."""
if url.path() == '/':
filepath = QUrlQuery(url).queryItemValue("file")
data = pdfjs.generate_pdfjs_page(QUrl.fromLocalFile(filepath))
return 'text/html', data
try: try:
data = pdfjs.get_pdfjs_res(url.path()) data = pdfjs.get_pdfjs_res(url.path())
except pdfjs.PDFJSNotFound as e: except pdfjs.PDFJSNotFound as e:
@ -534,5 +539,6 @@ def qute_pdfjs(url):
raise NotFoundError("Can't find pdfjs resource '{}'".format(e.path)) raise NotFoundError("Can't find pdfjs resource '{}'".format(e.path))
else: else:
mimetype, _encoding = mimetypes.guess_type(url.fileName()) mimetype, _encoding = mimetypes.guess_type(url.fileName())
assert mimetype is not None, url if mimetype is None:
mimetype = 'application/octet-stream'
return mimetype, data return mimetype, data

View File

@ -30,7 +30,7 @@ from PyQt5.QtPrintSupport import QPrintDialog
from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame from PyQt5.QtWebKitWidgets import QWebPage, QWebFrame
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.browser import pdfjs, shared from qutebrowser.browser import pdfjs, shared, downloads
from qutebrowser.browser.webkit import http from qutebrowser.browser.webkit import http
from qutebrowser.browser.webkit.network import networkmanager from qutebrowser.browser.webkit.network import networkmanager
from qutebrowser.utils import message, usertypes, log, jinja, objreg from qutebrowser.utils import message, usertypes, log, jinja, objreg
@ -275,10 +275,9 @@ class BrowserPage(QWebPage):
else: else:
reply.finished.connect(functools.partial( reply.finished.connect(functools.partial(
self.display_content, reply, 'image/jpeg')) self.display_content, reply, 'image/jpeg'))
elif (mimetype in ['application/pdf', 'application/x-pdf'] and elif pdfjs.should_use_pdfjs(mimetype):
config.val.content.pdfjs): download_manager.fetch(reply,
# Use pdf.js to display the page target=downloads.PDFJSDownloadTarget())
self._show_pdfjs(reply)
else: else:
# Unknown mimetype, so download anyways. # Unknown mimetype, so download anyways.
download_manager.fetch(reply, download_manager.fetch(reply,