Improve DjVu support
This commit is contained in:
@@ -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
|
||||
|
4
TODO
4
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
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user