diff --git a/README.md b/README.md index 7429bb5..908ccd6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ Bitcoin: 17jaxj26vFJNqQ2hEVerbBV5fpTusfqFro | Package | Version tested | Required for | | --- | --- | --- | | python-pymupdf | 1.14.5 | PDF support | -| python-numpy | 1.16.2 | DjVu support | | python-djvulibre | 0.8.4 | DjVu support | ## Support diff --git a/TODO b/TODO index 7b2c80e..ebfa9c1 100644 --- a/TODO +++ b/TODO @@ -89,6 +89,7 @@ TODO Have them save to memory ✓ fb2 support ✓ Images need to show up in their placeholders + ✓ djvu support Other: ✓ Define every widget in code Bugs: @@ -121,10 +122,11 @@ TODO Goodreads API: Ratings, Read, Recommendations Get ISBN using python-isbnlib Use embedded fonts + CSS - txt, doc, chm, djvu support + txt, doc, chm, markdown support Include icons for filetype emblems Comic view modes Continuous paging + Image rotation Ignore a / the / numbers for sorting purposes ? Add only one file type if multiple are present ? Create emblem per filetype diff --git a/lector/contentwidgets.py b/lector/contentwidgets.py index bea5abd..9620082 100644 --- a/lector/contentwidgets.py +++ b/lector/contentwidgets.py @@ -106,7 +106,7 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView): elif self.filetype == 'djvu': page_data = self.book.pages[page] - pixmap = render_djvu_page(page_data, '/tmp') + pixmap = render_djvu_page(page_data) return pixmap @@ -183,12 +183,10 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView): # TODO # Get caching working for double page view - # 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: diff --git a/lector/parsers/djvu.py b/lector/parsers/djvu.py index faa00f7..78f0112 100644 --- a/lector/parsers/djvu.py +++ b/lector/parsers/djvu.py @@ -17,33 +17,23 @@ 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): + def __init__(self, filename, *args): 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): + # TODO + # What even is this? title = os.path.basename(self.filename) author = 'Unknown' year = 9999 @@ -51,7 +41,7 @@ class ParseDJVU: tags = [] cover_page = self.book.pages[0] - cover = render_djvu_page(cover_page, self.extract_dir, True) + cover = render_djvu_page(cover_page, True) Metadata = collections.namedtuple( 'Metadata', ['title', 'author', 'year', 'isbn', 'tags', 'cover']) @@ -60,46 +50,49 @@ class ParseDJVU: 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): +def render_djvu_page(page, for_cover=False): # TODO # Figure out how to calculate image stride - bytes_per_line = 13200 + # and if it impacts row_alignment in the render + # method below + # bytes_per_line = 13200 - # Yes, but why? + 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 + + # ¯\_(ツ)_/¯ 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 + output = page_job.render( + mode, rect, rect, djvu_pixel_format) + # row_alignment=bytes_per_line) imageFormat = QtGui.QImage.Format_RGB32 - pageQImage = QtGui.QImage(color_buffer, width, height, imageFormat) + pageQImage = QtGui.QImage(output, width, height, imageFormat) + + # Format conversion not only keeps the damn thing from + # segfaulting when converting from QImage to QPixmap, + # but it also allows for the double page mode to keep + # working properly. We like format conversion. + pageQImage = pageQImage.convertToFormat( + QtGui.QImage.Format_ARGB32_Premultiplied) 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) + pixmap.convertFromImage(pageQImage) return pixmap diff --git a/lector/readers/read_epub.py b/lector/readers/read_epub.py index c7d92f4..124e8e5 100644 --- a/lector/readers/read_epub.py +++ b/lector/readers/read_epub.py @@ -89,7 +89,7 @@ class EPUB: return i # If the file isn't found - logger.error(filename + ' not found in ' + self.book_filename) + logger.warning(filename + ' not found in ' + self.book_filename) return False def generate_toc(self): @@ -109,7 +109,7 @@ class EPUB: if not toc_filename: if not toc_filename_alternative: - logger.error('No ToC found for: ' + self.book_filename) + logger.warning('No ToC found for: ' + self.book_filename) else: toc_filename = toc_filename_alternative diff --git a/lector/sorter.py b/lector/sorter.py index 27fffd9..0b4cf44 100644 --- a/lector/sorter.py +++ b/lector/sorter.py @@ -62,14 +62,13 @@ else: print(error_string) logger.error(error_string) -# numpy and djvu - Optional -numpy_check = importlib.util.find_spec('numpy') +# djvu - Optional djvu_check = importlib.util.find_spec('djvu.decode') -if numpy_check and djvu_check: +if 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.' + error_string = 'djvulibre is not installed. Will be unable to load Djvu files.' print(error_string) logger.error(error_string) diff --git a/lector/threaded.py b/lector/threaded.py index 79ea6d8..1da9a17 100644 --- a/lector/threaded.py +++ b/lector/threaded.py @@ -25,11 +25,17 @@ from PyQt5 import QtCore, QtGui from lector import sorter from lector import database +# The following have to be separate try: from lector.parsers.pdf import render_pdf_page except ImportError: pass +try: + from lector.parsers.djvu import render_djvu_page +except ImportError: + pass + logger = logging.getLogger(__name__) @@ -165,6 +171,10 @@ class BackGroundCacheRefill(QtCore.QThread): page_data = self.book.loadPage(current_page) pixmap = render_pdf_page(page_data) + elif self.filetype == 'djvu': + page_data = self.book.pages[current_page] + pixmap = render_djvu_page(page_data) + return pixmap remove_index = self.image_cache.index(self.remove_value)