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'): if not hasattr(timed_print, 'startup_time'):
timed_print.startup_time = monotonic() timed_print.startup_time = monotonic()
print(f'[{monotonic() - timed_print.startup_time:.2f}]', *a, **kw) 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) HTMLDisplay.__init__(self, parent=parent, save_resources_in_document=False)
self.vertical = vertical self.vertical = vertical
self.last_rendered_html = '', '', '' self.last_rendered_html = '', '', ''
self.base_url_for_current_book = None
self.anchor_clicked.connect(self.link_activated) self.anchor_clicked.connect(self.link_activated)
for x, icon in [ for x, icon in [
('remove_format', 'trash.png'), ('save_format', 'save.png'), ('remove_format', 'trash.png'), ('save_format', 'save.png'),
@ -1088,8 +1089,13 @@ class BookInfo(HTMLDisplay):
def show_data(self, mi): def show_data(self, mi):
html, table, comments = self.last_rendered_html = render_html(mi, self.vertical, self.parent()) 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) set_html(mi, html, self)
def get_base_qurl(self):
return self.base_url_for_current_book
def process_external_css(self, css): def process_external_css(self, css):
return resolve_colors(css) return resolve_colors(css)

View File

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

View File

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