diff --git a/TODO b/TODO
index 318df6e..7b2c80e 100644
--- a/TODO
+++ b/TODO
@@ -100,8 +100,10 @@ TODO
Search results should ignore punctuation
Keep text size for annotations
Sort by new is not working
+ Drag and drop is acting out
Secondary:
+ Navbar
Text to speech
Definitions dialog needs to respond to escape
Zoom slider for comics
diff --git a/lector/__main__.py b/lector/__main__.py
index cfbdf03..bbade4d 100755
--- a/lector/__main__.py
+++ b/lector/__main__.py
@@ -891,9 +891,12 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
last_accessed_time = QtCore.QDateTime().currentDateTime()
position_perc = 1
- self.lib_ref.libraryModel.setData(i, metadata, QtCore.Qt.UserRole + 3)
- self.lib_ref.libraryModel.setData(i, position_perc, QtCore.Qt.UserRole + 7)
- self.lib_ref.libraryModel.setData(i, last_accessed_time, QtCore.Qt.UserRole + 12)
+ self.lib_ref.libraryModel.setData(
+ i, metadata, QtCore.Qt.UserRole + 3)
+ self.lib_ref.libraryModel.setData(
+ i, position_perc, QtCore.Qt.UserRole + 7)
+ self.lib_ref.libraryModel.setData(
+ i, last_accessed_time, QtCore.Qt.UserRole + 12)
self.lib_ref.update_proxymodels()
database_dict = {
diff --git a/lector/contentwidgets.py b/lector/contentwidgets.py
index 5e483a2..bea5abd 100644
--- a/lector/contentwidgets.py
+++ b/lector/contentwidgets.py
@@ -25,6 +25,12 @@ try:
except ImportError:
pass
+try:
+ import djvu.decode
+ from lector.parsers.djvu import render_djvu_page
+except ImportError:
+ pass
+
from PyQt5 import QtWidgets, QtGui, QtCore
from lector.rarfile import rarfile
@@ -60,6 +66,11 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
elif self.filetype == 'pdf':
self.book = fitz.open(self.filepath)
+ elif self.filetype == 'djvu':
+ self.book = djvu.decode.Context().new_document(
+ djvu.decode.FileURI(self.filepath))
+ self.book.decoding_job.wait()
+
self.common_functions = PliantWidgetsCommonFunctions(
self, self.main_window)
@@ -93,6 +104,10 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
page_data = self.book.loadPage(page)
pixmap = render_pdf_page(page_data)
+ elif self.filetype == 'djvu':
+ page_data = self.book.pages[page]
+ pixmap = render_djvu_page(page_data, '/tmp')
+
return pixmap
firstPixmap = page_loader(current_page)
@@ -168,7 +183,15 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
# TODO
# Get caching working for double page view
- if not double_page_mode and self.main_window.settings['caching_enabled']:
+ # Get caching working for DjVu files
+
+ # All of these must be True
+ caching_conditions = (
+ not double_page_mode,
+ not self.filetype == 'djvu',
+ self.main_window.settings['caching_enabled'])
+
+ if False not in caching_conditions:
return_pixmap = None
while not return_pixmap:
return_pixmap = check_cache(current_page)
diff --git a/lector/dockwidgets.py b/lector/dockwidgets.py
index 0a91146..bce58d1 100644
--- a/lector/dockwidgets.py
+++ b/lector/dockwidgets.py
@@ -51,6 +51,12 @@ class PliantDockWidget(QtWidgets.QDockWidget):
# Except this one
self.sideDockTabWidget = None
+ # Animate appearance
+ self.animation = QtCore.QPropertyAnimation(self, b'windowOpacity')
+ self.animation.setStartValue(0)
+ self.animation.setEndValue(1)
+ self.animation.setDuration(200)
+
def showEvent(self, event=None):
viewport_topRight = self.contentView.mapToGlobal(
self.contentView.viewport().rect().topRight())
@@ -69,6 +75,7 @@ class PliantDockWidget(QtWidgets.QDockWidget):
self.main_window.active_docks.append(self)
self.setGeometry(dock_x, dock_y, dock_width, dock_height)
+ self.animation.start()
def hideEvent(self, event=None):
if self.notes_only:
diff --git a/lector/logger.py b/lector/logger.py
index 45f7128..953e014 100644
--- a/lector/logger.py
+++ b/lector/logger.py
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
-VERSION = '0.5.1'
+VERSION = '0.5.GittyGittyBangBang'
import os
import logging
diff --git a/lector/parsers/djvu.py b/lector/parsers/djvu.py
new file mode 100644
index 0000000..faa00f7
--- /dev/null
+++ b/lector/parsers/djvu.py
@@ -0,0 +1,105 @@
+# This file is a part of Lector, a Qt based ebook reader
+# Copyright (C) 2017-2019 BasioMeusPuga
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import os
+import collections
+
+import numpy
+import djvu.decode
+from PyQt5 import QtGui
+
+djvu_pixel_format = djvu.decode.PixelFormatRgbMask(0xFF0000, 0xFF00, 0xFF, bpp=32)
+djvu_pixel_format.rows_top_to_bottom = 1
+djvu_pixel_format.y_top_to_bottom = 0
+
+
+class ParseDJVU:
+ def __init__(self, filename, temp_dir, file_md5):
+ self.book = None
+ self.filename = filename
+
+ # Create the temporary directory where
+ # rendered pngs will be stored
+ # This may be skipped in case QImage to QPixmap conversion
+ # stops segfaulting
+ self.extract_dir = os.path.join(temp_dir, file_md5)
+ os.makedirs(self.extract_dir, exist_ok=True)
+
+ def read_book(self):
+ self.book = djvu.decode.Context().new_document(
+ djvu.decode.FileURI(self.filename))
+ self.book.decoding_job.wait()
+
+ def generate_metadata(self):
+ title = os.path.basename(self.filename)
+ author = 'Unknown'
+ year = 9999
+ isbn = None
+ tags = []
+
+ cover_page = self.book.pages[0]
+ cover = render_djvu_page(cover_page, self.extract_dir, True)
+
+ Metadata = collections.namedtuple(
+ 'Metadata', ['title', 'author', 'year', 'isbn', 'tags', 'cover'])
+ return Metadata(title, author, year, isbn, tags, cover)
+
+ def generate_content(self):
+ # TODO
+ # See if it's possible to generate a more involved ToC
+
+ content = list(range(len(self.book.pages)))
+ toc = [(1, f'Page {i + 1}', i + 1) for i in content]
+
+ # Return toc, content, images_only
+ return toc, content, True
+
+def render_djvu_page(page, extract_dir, for_cover=False):
+
+ # TODO
+ # Figure out how to calculate image stride
+ bytes_per_line = 13200
+
+ # Yes, but why?
+ mode = 0
+
+ page_job = page.decode(wait=True)
+ width, height = page_job.size
+ rect = (0, 0, width, height)
+ color_buffer = numpy.zeros((height, bytes_per_line), dtype=numpy.uint32)
+ page_job.render(
+ mode, rect, rect, djvu_pixel_format,
+ row_alignment=bytes_per_line,
+ buffer=color_buffer)
+ color_buffer ^= 0xFF000000
+
+ imageFormat = QtGui.QImage.Format_RGB32
+ pageQImage = QtGui.QImage(color_buffer, width, height, imageFormat)
+
+ if for_cover:
+ return pageQImage
+
+ # TODO
+ # Converting from the QImage to the QPixmap directly
+ # outright segfaults sometimes.
+ # This damages caching, speed and my ego
+
+ outfile = os.path.join(extract_dir, 'temporaryPNG.png')
+ pageQImage.save(outfile)
+ pixmap = QtGui.QPixmap()
+ pixmap.load(outfile)
+
+ return pixmap
diff --git a/lector/sorter.py b/lector/sorter.py
index 9789dd8..27fffd9 100644
--- a/lector/sorter.py
+++ b/lector/sorter.py
@@ -58,7 +58,18 @@ if mupdf_check:
from lector.parsers.pdf import ParsePDF
sorter['pdf'] = ParsePDF
else:
- error_string = 'pymupdf is not installed. Will be unable to load PDFs.'
+ error_string = 'pymupdf is not installed. Will be unable to load PDF files.'
+ print(error_string)
+ logger.error(error_string)
+
+# numpy and djvu - Optional
+numpy_check = importlib.util.find_spec('numpy')
+djvu_check = importlib.util.find_spec('djvu.decode')
+if numpy_check and djvu_check:
+ from lector.parsers.djvu import ParseDJVU
+ sorter['djvu'] = ParseDJVU
+else:
+ error_string = 'numpy / djvulibre is not installed. Will be unable to load Djvu files.'
print(error_string)
logger.error(error_string)
@@ -211,8 +222,6 @@ class BookSorter:
# None of the following have an exception type specified
# This will keep everything from crashing, but will make
# troubleshooting difficult
- # TODO
- # In application notifications
try:
book_ref.read_book()