mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
E-book viewer: Handle links to missing internal files with an error popup rather than becoming non-functional
This commit is contained in:
parent
c0f15325ed
commit
9d81359d8a
@ -13,7 +13,7 @@ from itertools import count
|
|||||||
|
|
||||||
from lxml.etree import Comment
|
from lxml.etree import Comment
|
||||||
|
|
||||||
from calibre import detect_ncpus, force_unicode, prepare_string_for_xml
|
from calibre import detect_ncpus, prepare_string_for_xml
|
||||||
from calibre.customize.ui import plugin_for_input_format
|
from calibre.customize.ui import plugin_for_input_format
|
||||||
from calibre.ebooks.oeb.base import EPUB, OEB_DOCS, OEB_STYLES, OPF, SMIL, XHTML, XHTML_NS, XLINK, rewrite_links, urlunquote
|
from calibre.ebooks.oeb.base import EPUB, OEB_DOCS, OEB_STYLES, OPF, SMIL, XHTML, XHTML_NS, XLINK, rewrite_links, urlunquote
|
||||||
from calibre.ebooks.oeb.base import XPath as _XPath
|
from calibre.ebooks.oeb.base import XPath as _XPath
|
||||||
@ -33,7 +33,7 @@ from polyglot.binary import as_base64_unicode as encode_component
|
|||||||
from polyglot.binary import from_base64_bytes
|
from polyglot.binary import from_base64_bytes
|
||||||
from polyglot.binary import from_base64_unicode as decode_component
|
from polyglot.binary import from_base64_unicode as decode_component
|
||||||
from polyglot.builtins import as_bytes
|
from polyglot.builtins import as_bytes
|
||||||
from polyglot.urllib import quote, urlparse
|
from polyglot.urllib import urlparse
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from calibre_extensions.speedup import get_num_of_significant_chars
|
from calibre_extensions.speedup import get_num_of_significant_chars
|
||||||
@ -98,9 +98,7 @@ def create_link_replacer(container, link_uid, changed):
|
|||||||
frag = urlunquote(frag)
|
frag = urlunquote(frag)
|
||||||
url = resource_template.format(encode_url(name, frag))
|
url = resource_template.format(encode_url(name, frag))
|
||||||
else:
|
else:
|
||||||
if isinstance(name, str):
|
url = 'missing:' + name
|
||||||
name = name.encode('utf-8')
|
|
||||||
url = 'missing:' + force_unicode(quote(name), 'utf-8')
|
|
||||||
changed.add(base)
|
changed.add(base)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
@ -475,14 +473,18 @@ def transform_html(container, name, virtualize_resources, link_uid, link_to_map,
|
|||||||
href = link_replacer(name, href)
|
href = link_replacer(name, href)
|
||||||
elif attr in a.attrib:
|
elif attr in a.attrib:
|
||||||
a.set(attr, 'javascript:void(0)')
|
a.set(attr, 'javascript:void(0)')
|
||||||
if href and href.startswith(link_uid):
|
if href:
|
||||||
a.set(attr, 'javascript:void(0)')
|
if href.startswith(link_uid):
|
||||||
parts = href.split('|')
|
a.set(attr, 'javascript:void(0)')
|
||||||
if len(parts) > 1:
|
parts = href.split('|')
|
||||||
parts = decode_url(parts[1])
|
if len(parts) > 1:
|
||||||
lname, lfrag = parts[0], parts[1]
|
parts = decode_url(parts[1])
|
||||||
link_to_map.setdefault(lname, {}).setdefault(lfrag or '', set()).add(name)
|
lname, lfrag = parts[0], parts[1]
|
||||||
a.set('data-' + link_uid, json.dumps({'name':lname, 'frag':lfrag}, ensure_ascii=False))
|
link_to_map.setdefault(lname, {}).setdefault(lfrag or '', set()).add(name)
|
||||||
|
a.set('data-' + link_uid, json.dumps({'name':lname, 'frag':lfrag}, ensure_ascii=False))
|
||||||
|
elif href.startswith('missing:'):
|
||||||
|
a.set(attr, 'javascript:void(0)')
|
||||||
|
a.set('data-' + link_uid, json.dumps({'name':href[len('missing:'):], 'frag':'', 'missing': True}, ensure_ascii=False))
|
||||||
|
|
||||||
for a in link_xpath(root):
|
for a in link_xpath(root):
|
||||||
handle_link(a)
|
handle_link(a)
|
||||||
@ -519,8 +521,12 @@ def virtualize_html(container, name, link_uid, link_to_map, virtualized_names):
|
|||||||
link_to_map.setdefault(lname, {}).setdefault(lfrag or '', set()).add(name)
|
link_to_map.setdefault(lname, {}).setdefault(lfrag or '', set()).add(name)
|
||||||
a.set('data-' + link_uid, json.dumps({'name':lname, 'frag':lfrag}, ensure_ascii=False))
|
a.set('data-' + link_uid, json.dumps({'name':lname, 'frag':lfrag}, ensure_ascii=False))
|
||||||
elif href:
|
elif href:
|
||||||
a.set('target', '_blank')
|
if href.startswith('missing:'):
|
||||||
a.set('rel', 'noopener noreferrer')
|
a.set(attr, 'javascript:void(0)')
|
||||||
|
a.set('data-' + link_uid, json.dumps({'name':href[len('missing:'):], 'frag':'', 'missing': True}, ensure_ascii=False))
|
||||||
|
else:
|
||||||
|
a.set('target', '_blank')
|
||||||
|
a.set('rel', 'noopener noreferrer')
|
||||||
elif attr in a.attrib:
|
elif attr in a.attrib:
|
||||||
a.set(attr, 'javascript:void(0)')
|
a.set(attr, 'javascript:void(0)')
|
||||||
|
|
||||||
@ -978,7 +984,7 @@ def develop(max_workers=1, wait_for_input=True):
|
|||||||
with TemporaryDirectory() as tdir:
|
with TemporaryDirectory() as tdir:
|
||||||
render(
|
render(
|
||||||
path, tdir, serialize_metadata=True,
|
path, tdir, serialize_metadata=True,
|
||||||
extract_annotations=True, virtualize_resources=True, max_workers=max_workers
|
extract_annotations=True, virtualize_resources=False, max_workers=max_workers
|
||||||
)
|
)
|
||||||
print('Extracted to:', tdir)
|
print('Extracted to:', tdir)
|
||||||
if wait_for_input:
|
if wait_for_input:
|
||||||
|
@ -745,7 +745,10 @@ class IframeBoss:
|
|||||||
except:
|
except:
|
||||||
print('WARNING: Failed to parse link data {}, ignoring'.format(evt.currentTarget?.getAttribute?(self.link_attr)))
|
print('WARNING: Failed to parse link data {}, ignoring'.format(evt.currentTarget?.getAttribute?(self.link_attr)))
|
||||||
return
|
return
|
||||||
self.activate_link(data.name, data.frag, evt.currentTarget)
|
if data.missing:
|
||||||
|
self.send_message('link_to_missing_activated', name=data.name)
|
||||||
|
else:
|
||||||
|
self.activate_link(data.name, data.frag, evt.currentTarget)
|
||||||
|
|
||||||
def activate_link(self, name, frag, target_elem):
|
def activate_link(self, name, frag, target_elem):
|
||||||
if not name:
|
if not name:
|
||||||
|
@ -254,6 +254,7 @@ class View:
|
|||||||
'report_cfi': self.on_report_cfi,
|
'report_cfi': self.on_report_cfi,
|
||||||
'request_size': self.on_request_size,
|
'request_size': self.on_request_size,
|
||||||
'scroll_to_anchor': self.on_scroll_to_anchor,
|
'scroll_to_anchor': self.on_scroll_to_anchor,
|
||||||
|
'link_to_missing_activated': self.on_link_to_missing_activated,
|
||||||
'selectionchange': self.on_selection_change,
|
'selectionchange': self.on_selection_change,
|
||||||
'update_selection_position': self.update_selection_position,
|
'update_selection_position': self.update_selection_position,
|
||||||
'columns_per_screen_changed': self.on_columns_per_screen_changed,
|
'columns_per_screen_changed': self.on_columns_per_screen_changed,
|
||||||
@ -1122,6 +1123,9 @@ class View:
|
|||||||
frag = item.frag or ''
|
frag = item.frag or ''
|
||||||
self.show_name(name, initial_position={'type':'pagelist_ref', 'anchor':frag, 'replace_history':False})
|
self.show_name(name, initial_position={'type':'pagelist_ref', 'anchor':frag, 'replace_history':False})
|
||||||
|
|
||||||
|
def on_link_to_missing_activated(self, data):
|
||||||
|
ui_operations.show_error(_('Invalid link'), _('This link points to the file {} which does not exist in the book').format(data.name))
|
||||||
|
|
||||||
def on_scroll_to_anchor(self, data):
|
def on_scroll_to_anchor(self, data):
|
||||||
self.show_name(data.name, initial_position={'type':'anchor', 'anchor':data.frag, 'replace_history':False})
|
self.show_name(data.name, initial_position={'type':'anchor', 'anchor':data.frag, 'replace_history':False})
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user