Windows: Fix a regression in 7.0 that caused images referring to files on the disk within comments columns to not display in some circumstances

This commit is contained in:
Kovid Goyal 2024-02-04 09:18:30 +05:30
parent 7e3ac04222
commit 90a0a33723
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 84 additions and 47 deletions

View File

@ -1680,3 +1680,23 @@ def timed_print(*a, **kw):
if not hasattr(timed_print, 'startup_time'):
timed_print.startup_time = monotonic()
print(f'[{monotonic() - timed_print.startup_time:.2f}]', *a, **kw)
def local_path_for_resource(qurl: QUrl, base_qurl: 'QUrl | None' = None) -> str:
import re
def fix_qt_bodging_windows_paths(path: str) -> str:
# When loading <img src="file:///c:/path/to/img.png"> Qt gives us the
# URL: //c/path/to/img.png Le bubbling sigh
if iswindows and re.match(r'//[a-zA-Z]/', path) is not None and not os.path.exists(path):
path = os.path.normpath(path[2] + ':' + path[3:])
return path
if base_qurl and qurl.isRelative():
qurl = base_qurl.resolved(qurl)
if qurl.isLocalFile():
return fix_qt_bodging_windows_paths(qurl.toLocalFile())
if qurl.isRelative(): # this means has no scheme
return fix_qt_bodging_windows_paths(qurl.path())
return ''

View File

@ -998,6 +998,7 @@ class BookInfo(HTMLDisplay):
HTMLDisplay.__init__(self, parent=parent, save_resources_in_document=False)
self.vertical = vertical
self.last_rendered_html = '', '', ''
self.base_url_for_current_book = None
self.anchor_clicked.connect(self.link_activated)
for x, icon in [
('remove_format', 'trash.png'), ('save_format', 'save.png'),
@ -1088,8 +1089,13 @@ class BookInfo(HTMLDisplay):
def show_data(self, mi):
html, table, comments = self.last_rendered_html = render_html(mi, self.vertical, self.parent())
path = getattr(mi, 'path', None)
self.base_url_for_current_book = QUrl.fromLocalFile(os.path.join(path, 'metadata.opf')) if path else None
set_html(mi, html, self)
def get_base_qurl(self):
return self.base_url_for_current_book
def process_external_css(self, css):
return resolve_colors(css)

View File

@ -7,7 +7,6 @@ import re
import sys
import weakref
from collections import defaultdict
from threading import Thread
from contextlib import contextmanager
from functools import partial
from html5_parser import parse
@ -21,15 +20,16 @@ from qt.core import (
QTextFrameFormat, QTextImageFormat, QTextListFormat, QTimer, QToolButton, QUrl,
QVBoxLayout, QWidget, pyqtSignal, pyqtSlot,
)
from threading import Thread
from calibre import browser, fit_image, xml_replace_entities
from calibre.constants import iswindows
from calibre.db.constants import DATA_DIR_NAME
from calibre.ebooks.chardet import xml_to_unicode
from calibre.gui2 import (
NO_URL_FORMATTING, choose_dir, choose_files, error_dialog, gprefs, is_dark_theme,
question_dialog, safe_open_url,
NO_URL_FORMATTING, FunctionDispatcher, choose_dir, choose_files, error_dialog,
gprefs, is_dark_theme, local_path_for_resource, question_dialog, safe_open_url,
)
from calibre.gui2 import FunctionDispatcher
from calibre.gui2.book_details import resolved_css
from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2.flow_toolbar import create_flow_toolbar
@ -38,6 +38,7 @@ from calibre.gui2.widgets2 import to_plain_text
from calibre.startup import connect_lambda
from calibre.utils.cleantext import clean_xml_chars
from calibre.utils.config import tweaks
from calibre.utils.filenames import make_long_path_useable
from calibre.utils.imghdr import what
from polyglot.builtins import iteritems, itervalues
@ -929,14 +930,11 @@ class EditorWidget(QTextEdit, LineEditECM): # {{{
@pyqtSlot(int, 'QUrl', result='QVariant')
def loadResource(self, rtype, qurl):
if self.base_url:
if qurl.isRelative():
qurl = self.base_url.resolved(qurl)
if qurl.isLocalFile():
path = local_path_for_resource(qurl, base_qurl=self.base_url)
if path:
data = None
path = qurl.toLocalFile()
try:
with open(path, 'rb') as f:
with open(make_long_path_useable(path), 'rb') as f:
data = f.read()
except OSError:
if path.rpartition('.')[-1].lower() in {'jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'}:

View File

@ -129,6 +129,9 @@ class Details(HTMLDisplay):
self.allow_context_menu = allow_context_menu
self.is_locked = is_locked
def get_base_qurl(self):
return getattr(self.book_info, 'base_url_for_current_book', None)
def sizeHint(self):
return QSize(350, 350)

View File

@ -16,7 +16,9 @@ from qt.core import (
from calibre import prepare_string_for_xml
from calibre.constants import builtin_colors_dark, builtin_colors_light
from calibre.ebooks.metadata import rating_to_stars
from calibre.gui2 import UNDEFINED_QDATETIME, gprefs, rating_font
from calibre.gui2 import (
UNDEFINED_QDATETIME, gprefs, local_path_for_resource, rating_font,
)
from calibre.gui2.complete2 import EditWithComplete, LineEdit
from calibre.gui2.widgets import history
from calibre.utils.config_base import tweaks
@ -543,6 +545,9 @@ class HTMLDisplay(QTextBrowser):
self.setAcceptDrops(False)
self.anchorClicked.connect(self.on_anchor_clicked)
def get_base_qurl(self):
return None
def setHtml(self, html):
self.last_set_html = html
QTextBrowser.setHtml(self, html)
@ -573,11 +578,10 @@ class HTMLDisplay(QTextBrowser):
return
self.anchor_clicked.emit(qurl)
def loadResource(self, rtype, qurl):
if qurl.isLocalFile():
path = qurl.toLocalFile()
def load_local_file_resource(self, rtype, qurl, path):
from calibre.utils.filenames import make_long_path_useable
try:
with open(path, 'rb') as f:
with open(make_long_path_useable(path), 'rb') as f:
data = f.read()
except OSError:
if path.rpartition('.')[-1].lower() in {'jpg', 'jpeg', 'gif', 'png', 'bmp', 'webp'}:
@ -595,7 +599,13 @@ class HTMLDisplay(QTextBrowser):
if self.save_resources_in_document:
self.document().addResource(rtype, qurl, r)
return r
elif qurl.scheme() == 'calibre-icon':
return super().loadResource(rtype, qurl)
def loadResource(self, rtype, qurl):
path = local_path_for_resource(qurl, base_qurl=self.get_base_qurl())
if path:
return self.load_local_file_resource(rtype, qurl, path)
if qurl.scheme() == 'calibre-icon':
r = QIcon.icon_as_png(qurl.path().lstrip('/'), as_bytearray=True)
self.document().addResource(rtype, qurl, r)
return r
@ -611,7 +621,7 @@ class HTMLDisplay(QTextBrowser):
self.document().addResource(rtype, qurl, r)
return r
else:
return QTextBrowser.loadResource(self, rtype, qurl)
return super().loadResource(rtype, qurl)
def anchorAt(self, pos):
# Anchors in a document can be "focused" with the tab key.