Work on viewer setting for background image

This commit is contained in:
Kovid Goyal 2019-09-24 20:21:25 +05:30
parent 62d38e6706
commit 92ffeebb4a
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 123 additions and 13 deletions

View File

@ -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():

View File

@ -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)

View File

@ -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):

View File

@ -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()

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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'