mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Work on viewer setting for background image
This commit is contained in:
parent
62d38e6706
commit
92ffeebb4a
@ -17,7 +17,6 @@ from PyQt5.Qt import (
|
||||
)
|
||||
|
||||
from calibre import prints
|
||||
from calibre.constants import config_dir
|
||||
from calibre.customize.ui import available_input_formats
|
||||
from calibre.gui2 import choose_files, error_dialog
|
||||
from calibre.gui2.image_popup import ImagePopup
|
||||
@ -30,14 +29,15 @@ from calibre.gui2.viewer.convert_book import prepare_book, update_book
|
||||
from calibre.gui2.viewer.lookup import Lookup
|
||||
from calibre.gui2.viewer.toc import TOC, TOCSearch, TOCView
|
||||
from calibre.gui2.viewer.web_view import (
|
||||
WebView, get_path_for_name, get_session_pref, set_book_path, vprefs
|
||||
WebView, get_path_for_name, get_session_pref, set_book_path, viewer_config_dir,
|
||||
vprefs
|
||||
)
|
||||
from calibre.utils.date import utcnow
|
||||
from calibre.utils.ipc.simple_worker import WorkerError
|
||||
from calibre.utils.serialize import json_loads
|
||||
from polyglot.builtins import as_bytes, itervalues
|
||||
|
||||
annotations_dir = os.path.join(config_dir, 'viewer', 'annots')
|
||||
annotations_dir = os.path.join(viewer_config_dir, 'annots')
|
||||
|
||||
|
||||
def dock_defs():
|
||||
|
@ -5,6 +5,7 @@
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from itertools import count
|
||||
|
||||
@ -19,11 +20,12 @@ from PyQt5.QtWebEngineWidgets import (
|
||||
|
||||
from calibre import as_unicode, prints
|
||||
from calibre.constants import (
|
||||
FAKE_HOST, FAKE_PROTOCOL, __version__, is_running_from_develop, isosx, iswindows
|
||||
FAKE_HOST, FAKE_PROTOCOL, __version__, config_dir, is_running_from_develop,
|
||||
isosx, iswindows
|
||||
)
|
||||
from calibre.ebooks.metadata.book.base import field_metadata
|
||||
from calibre.ebooks.oeb.polish.utils import guess_type
|
||||
from calibre.gui2 import error_dialog, safe_open_url
|
||||
from calibre.gui2 import choose_images, error_dialog, safe_open_url
|
||||
from calibre.gui2.webengine import (
|
||||
Bridge, RestartingWebEngineView, create_script, from_js, insert_scripts,
|
||||
secure_webengine, to_js
|
||||
@ -31,7 +33,7 @@ from calibre.gui2.webengine import (
|
||||
from calibre.srv.code import get_translations_data
|
||||
from calibre.utils.config import JSONConfig
|
||||
from calibre.utils.serialize import json_loads
|
||||
from polyglot.builtins import iteritems
|
||||
from polyglot.builtins import as_bytes, iteritems
|
||||
|
||||
try:
|
||||
from PyQt5 import sip
|
||||
@ -39,6 +41,7 @@ except ImportError:
|
||||
import sip
|
||||
|
||||
vprefs = JSONConfig('viewer-webengine')
|
||||
viewer_config_dir = os.path.join(config_dir, 'viewer')
|
||||
vprefs.defaults['session_data'] = {}
|
||||
vprefs.defaults['main_window_state'] = None
|
||||
vprefs.defaults['main_window_geometry'] = None
|
||||
@ -77,6 +80,20 @@ def get_data(name):
|
||||
return None, None
|
||||
|
||||
|
||||
def background_image():
|
||||
ans = getattr(background_image, 'ans', None)
|
||||
if ans is None:
|
||||
img_path = os.path.join(viewer_config_dir, 'bg-image.data')
|
||||
if os.path.exists(img_path):
|
||||
with open(img_path, 'rb') as f:
|
||||
data = f.read()
|
||||
mt, data = data.split(b'|', 1)
|
||||
else:
|
||||
ans = b'image/jpeg', b''
|
||||
ans = background_image.ans = mt.decode('utf-8'), data
|
||||
return ans
|
||||
|
||||
|
||||
def send_reply(rq, mime_type, data):
|
||||
if sip.isdeleted(rq):
|
||||
return
|
||||
@ -131,6 +148,12 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
|
||||
elif name == 'manifest':
|
||||
data = b'[' + set_book_path.manifest + b',' + set_book_path.metadata + b']'
|
||||
send_reply(rq, set_book_path.manifest_mime, data)
|
||||
elif name == 'reader-background':
|
||||
mt, data = background_image()
|
||||
if data:
|
||||
send_reply(rq, mt, data)
|
||||
else:
|
||||
rq.fail(rq.UrlNotFound)
|
||||
elif name.startswith('mathjax/'):
|
||||
from calibre.gui2.viewer.mathjax import monkeypatch_mathjax
|
||||
if name == 'mathjax/manifest.json':
|
||||
@ -206,6 +229,7 @@ class ViewerBridge(Bridge):
|
||||
selection_changed = from_js(object)
|
||||
copy_selection = from_js(object)
|
||||
view_image = from_js(object)
|
||||
change_background_image = from_js(object)
|
||||
|
||||
create_view = to_js()
|
||||
show_preparing_message = to_js()
|
||||
@ -215,6 +239,7 @@ class ViewerBridge(Bridge):
|
||||
full_screen_state_changed = to_js()
|
||||
get_current_cfi = to_js()
|
||||
show_home_page = to_js()
|
||||
background_image_changed = to_js()
|
||||
|
||||
|
||||
def apply_font_settings(page_or_view):
|
||||
@ -369,6 +394,7 @@ class WebView(RestartingWebEngineView):
|
||||
self.bridge.selection_changed.connect(self.selection_changed)
|
||||
self.bridge.view_image.connect(self.view_image)
|
||||
self.bridge.report_cfi.connect(self.call_callback)
|
||||
self.bridge.change_background_image.connect(self.change_background_image)
|
||||
self.pending_bridge_ready_actions = {}
|
||||
self.setPage(self._page)
|
||||
self.setAcceptDrops(False)
|
||||
@ -483,3 +509,13 @@ class WebView(RestartingWebEngineView):
|
||||
|
||||
def show_home_page(self):
|
||||
self.execute_when_ready('show_home_page')
|
||||
|
||||
def change_background_image(self, img_id):
|
||||
files = choose_images(self, 'viewer-background-image', _('Choose background image'), formats=['png', 'gif', 'jpg', 'jpeg'])
|
||||
if files:
|
||||
img = files[0]
|
||||
with open(img, 'rb') as src, open(os.path.join(viewer_config_dir, 'bg-image.data'), 'wb') as dest:
|
||||
dest.write(as_bytes(guess_type(img)[0] or 'image/jpeg') + b'|')
|
||||
shutil.copyfileobj(src, dest)
|
||||
background_image.ans = None
|
||||
self.execute_when_ready('background_image_changed', img_id)
|
||||
|
@ -69,7 +69,7 @@ class Prefs:
|
||||
items = [
|
||||
create_item(_('Colors'), def():self.show_panel('colors');, _('Colors of the page and text')),
|
||||
create_item(_('Page layout'), def():self.show_panel('layout');, _('Page margins and number of pages per screen')),
|
||||
create_item(_('User style sheet'), def():self.show_panel('user_stylesheet');, _('Style rules for text')),
|
||||
create_item(_('Styles'), def():self.show_panel('user_stylesheet');, _('Style rules for text and background image')),
|
||||
create_item(_('Headers and footers'), def():self.show_panel('head_foot');, _('Customize the headers and footers')),
|
||||
create_item(_('Keyboard shortcuts'), def():self.show_panel('keyboard');, _('Customize the keyboard shortcuts')),
|
||||
]
|
||||
@ -109,7 +109,7 @@ class Prefs:
|
||||
commit_layout(self.onchange, self.container)
|
||||
|
||||
def display_user_stylesheet(self, container):
|
||||
document.getElementById(self.title_id).textContent = _('User style sheet')
|
||||
document.getElementById(self.title_id).textContent = _('Styles')
|
||||
create_user_stylesheet_panel(container)
|
||||
|
||||
def close_user_stylesheet(self):
|
||||
|
@ -6,18 +6,54 @@ from elementmaker import E
|
||||
from gettext import gettext as _
|
||||
|
||||
from book_list.globals import get_session_data
|
||||
from read_book.globals import runtime
|
||||
from read_book.globals import runtime, ui_operations
|
||||
from viewer.constants import READER_BACKGROUND_URL
|
||||
from widgets import create_button
|
||||
from dom import unique_id
|
||||
|
||||
|
||||
BLANK = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
|
||||
|
||||
|
||||
def change_background_image(img_id):
|
||||
ui_operations.change_background_image(img_id)
|
||||
|
||||
|
||||
def clear_image(img_id):
|
||||
document.getElementById(img_id).src = BLANK
|
||||
|
||||
|
||||
def background_widget(sd):
|
||||
if sd.get('background_image'):
|
||||
src = READER_BACKGROUND_URL
|
||||
else:
|
||||
src = BLANK
|
||||
img_id = unique_id('bg-image')
|
||||
|
||||
return E.div(
|
||||
style='display: flex; align-items: center',
|
||||
E.div(E.img(src=src, id=img_id, class_='bg-image-preview', style='width: 75px; height: 75px; border: solid 1px')),
|
||||
E.div('\xa0', style='margin: 0.5rem'),
|
||||
create_button(_('Change image'), action=change_background_image.bind(None, img_id)),
|
||||
E.div('\xa0', style='margin: 0.5rem'),
|
||||
create_button(_('Clear image'), action=clear_image.bind(None, img_id)),
|
||||
)
|
||||
|
||||
|
||||
def create_user_stylesheet_panel(container):
|
||||
sd = get_session_data()
|
||||
container.appendChild(
|
||||
E.div(
|
||||
style='min-height: 80vh; display: flex; flex-flow: column; margin: 1ex 1rem; padding: 1ex 0',
|
||||
style='min-height: 75vh; display: flex; flex-flow: column; margin: 1ex 1rem; padding: 1ex 0',
|
||||
E.div(
|
||||
style='border-bottom: solid 1px; margin-bottom: 1.5ex; padding-bottom: 1.5ex',
|
||||
E.div(_('Choose a background image to display behind the book text'), style='margin-bottom: 1.5ex'),
|
||||
background_widget(sd),
|
||||
),
|
||||
E.div(
|
||||
style='flex-grow: 10; display: flex; flex-flow: column',
|
||||
E.div(
|
||||
_('A CSS style sheet that can be used to control the look and feel of books. For examples, click'), ' ',
|
||||
_('A CSS style sheet that can be used to control the look and feel of the text. For examples, click'), ' ',
|
||||
E.a(class_='blue-link', title=_("Examples of user style sheets"),
|
||||
target=('_self' if runtime.is_standalone_viewer else '_blank'),
|
||||
href='https://www.mobileread.com/forums/showthread.php?t=51500', _('here'))
|
||||
@ -38,6 +74,16 @@ def commit_user_stylesheet(onchange, container):
|
||||
ta = container.querySelector('[name=user-stylesheet]')
|
||||
val = ta.value or ''
|
||||
old = sd.get('user_stylesheet')
|
||||
changed = False
|
||||
if old is not val:
|
||||
sd.set('user_stylesheet', val)
|
||||
changed = True
|
||||
bg_image = container.querySelector('img.bg-image-preview').src
|
||||
if bg_image is BLANK:
|
||||
bg_image = None
|
||||
old = sd.get('background_image')
|
||||
if old is not bg_image:
|
||||
sd.set('background_image', bg_image)
|
||||
changed = True
|
||||
if changed:
|
||||
onchange()
|
||||
|
@ -29,7 +29,10 @@ from read_book.timers import Timers
|
||||
from read_book.toc import get_current_toc_nodes, update_visible_toc_nodes
|
||||
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
||||
from session import get_device_uuid, get_interface_data
|
||||
from utils import html_escape, is_ios, parse_url_params, username_key, safe_set_inner_html
|
||||
from utils import (
|
||||
html_escape, is_ios, parse_url_params, safe_set_inner_html, username_key
|
||||
)
|
||||
from viewer.constants import READER_BACKGROUND_URL
|
||||
|
||||
add_extra_css(def():
|
||||
sel = '.book-side-margin'
|
||||
@ -435,7 +438,17 @@ class View:
|
||||
s = m.style
|
||||
s.color = ans.foreground
|
||||
s.backgroundColor = ans.background
|
||||
sd = get_session_data()
|
||||
self.iframe.style.backgroundColor = ans.background or 'white'
|
||||
bg_image = sd.get('background_image')
|
||||
if bg_image:
|
||||
if runtime.is_standalone_viewer:
|
||||
self.iframe.style.backgroundImage = f'url({READER_BACKGROUND_URL})'
|
||||
else:
|
||||
self.iframe.style.backgroundImage = f'url({bg_image})'
|
||||
else:
|
||||
self.iframe.style.backgroundImage = 'none'
|
||||
|
||||
m.parentNode.style.backgroundColor = ans.background # this is needed on iOS where the bottom margin has its own margin, so we dont want the body background color to bleed through
|
||||
self.content_popup_overlay.apply_color_scheme(ans.background, ans.foreground)
|
||||
return ans
|
||||
|
@ -34,6 +34,8 @@ defaults = {
|
||||
'max_text_width': 0,
|
||||
'columns_per_screen': {'portrait':0, 'landscape':0},
|
||||
'user_stylesheet': '',
|
||||
'background_image': None,
|
||||
'background_image_style': 'stretch',
|
||||
'current_color_scheme': 'white',
|
||||
'user_color_schemes': {},
|
||||
'base_font_size': 16,
|
||||
@ -58,6 +60,8 @@ is_local_setting = {
|
||||
'max_text_width': True,
|
||||
'columns_per_screen': True,
|
||||
'user_stylesheet': True,
|
||||
'background_image': True,
|
||||
'background_image_style': True,
|
||||
'current_color_scheme': True,
|
||||
'base_font_size': True,
|
||||
'controls_help_shown_count': True,
|
||||
|
@ -22,7 +22,7 @@ from read_book.shortcuts import add_standalone_viewer_shortcuts
|
||||
from read_book.view import View
|
||||
from session import session_defaults
|
||||
from utils import encode_query_with_path, parse_url_params
|
||||
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL
|
||||
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL, READER_BACKGROUND_URL
|
||||
|
||||
runtime.is_standalone_viewer = True
|
||||
runtime.FAKE_HOST = FAKE_HOST
|
||||
@ -237,6 +237,14 @@ def get_current_cfi(request_id):
|
||||
view.get_current_cfi(request_id, ui_operations.report_cfi)
|
||||
|
||||
|
||||
@from_python
|
||||
def background_image_changed(img_id):
|
||||
img = document.getElementById(img_id)
|
||||
if img:
|
||||
img.src = ''
|
||||
img.src = READER_BACKGROUND_URL
|
||||
|
||||
|
||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||
if not error_object:
|
||||
# cross domain error
|
||||
@ -295,6 +303,8 @@ if window is window.top:
|
||||
to_python.copy_selection(text or None)
|
||||
ui_operations.view_image = def(name):
|
||||
to_python.view_image(name)
|
||||
ui_operations.change_background_image = def(img_id):
|
||||
to_python.change_background_image(img_id)
|
||||
|
||||
document.body.appendChild(E.div(id='view'))
|
||||
window.onerror = onerror
|
||||
|
@ -5,3 +5,4 @@ from __python__ import bound_methods, hash_literals
|
||||
|
||||
FAKE_PROTOCOL = '__FAKE_PROTOCOL__'
|
||||
FAKE_HOST = '__FAKE_HOST__'
|
||||
READER_BACKGROUND_URL = f'{FAKE_PROTOCOL}://{FAKE_HOST}/reader-background'
|
||||
|
Loading…
x
Reference in New Issue
Block a user