mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Append and modify CSS when sending epubs to Kobo devices
This allows users to have a "kobo_extra.css" file in the root of their device containing CSS rules. This will be appended to all stylesheets in the epub. As well, if the extra rules contain an @page rule, any existing @page rules will be stripped from the stylesheets. Finally, if any of the extra rules include "widows" and "orphans" settings, these are stripped from the rules in the stylesheets.
This commit is contained in:
parent
d38dffa86d
commit
7cbad00c25
@ -28,6 +28,15 @@ from calibre.ptempfile import PersistentTemporaryFile
|
|||||||
from calibre.constants import DEBUG
|
from calibre.constants import DEBUG
|
||||||
from calibre.utils.config_base import prefs
|
from calibre.utils.config_base import prefs
|
||||||
|
|
||||||
|
EPUB_EXT = '.epub'
|
||||||
|
|
||||||
|
|
||||||
|
class DummyCSSPreProcessor(object):
|
||||||
|
|
||||||
|
def __call__(self, data, add_namespace=False):
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class KOBO(USBMS):
|
class KOBO(USBMS):
|
||||||
|
|
||||||
@ -35,7 +44,7 @@ class KOBO(USBMS):
|
|||||||
gui_name = 'Kobo Reader'
|
gui_name = 'Kobo Reader'
|
||||||
description = _('Communicate with the Kobo Reader')
|
description = _('Communicate with the Kobo Reader')
|
||||||
author = 'Timothy Legge and David Forrester'
|
author = 'Timothy Legge and David Forrester'
|
||||||
version = (2, 0, 13)
|
version = (2, 1, 0)
|
||||||
|
|
||||||
dbversion = 0
|
dbversion = 0
|
||||||
fwversion = 0
|
fwversion = 0
|
||||||
@ -1228,6 +1237,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
book_class = Book
|
book_class = Book
|
||||||
|
|
||||||
MAX_PATH_LEN = 185 # 250 - (len(" - N3_LIBRARY_SHELF.parsed") + len("F:\.kobo\images\"))
|
MAX_PATH_LEN = 185 # 250 - (len(" - N3_LIBRARY_SHELF.parsed") + len("F:\.kobo\images\"))
|
||||||
|
KOBO_EXTRA_CSSFILE = 'kobo_extra.css'
|
||||||
|
|
||||||
EXTRA_CUSTOMIZATION_MESSAGE = [
|
EXTRA_CUSTOMIZATION_MESSAGE = [
|
||||||
_('The Kobo Touch from firmware V2.0.0 supports bookshelves.')+\
|
_('The Kobo Touch from firmware V2.0.0 supports bookshelves.')+\
|
||||||
@ -1259,6 +1269,11 @@ class KOBOTOUCH(KOBO):
|
|||||||
'This is not read by the device from the sideloaded books. '
|
'This is not read by the device from the sideloaded books. '
|
||||||
'Series information can only be added to the device after the book has been processed by the device. '
|
'Series information can only be added to the device after the book has been processed by the device. '
|
||||||
'Enable if you wish to set series information.'),
|
'Enable if you wish to set series information.'),
|
||||||
|
_('Modify CSS') +
|
||||||
|
':::'+_('This allows addition of user CSS rules and removal of some CSS. '
|
||||||
|
'When sending a book, the driver adds the contents of ' + KOBO_EXTRA_CSSFILE + ' to all stylesheets in the ePub. '
|
||||||
|
'This file is searched for in the root directory of the main memory of the device. '
|
||||||
|
'As well as this, if the file contains settings for the "orphans" or "widows", these are removed for all styles in the original stylesheet.'),
|
||||||
_('Attempt to support newer firmware') +
|
_('Attempt to support newer firmware') +
|
||||||
':::'+_('Kobo routinely updates the firmware and the '
|
':::'+_('Kobo routinely updates the firmware and the '
|
||||||
'database version. With this option Calibre will attempt '
|
'database version. With this option Calibre will attempt '
|
||||||
@ -1284,6 +1299,7 @@ class KOBOTOUCH(KOBO):
|
|||||||
False,
|
False,
|
||||||
False,
|
False,
|
||||||
False,
|
False,
|
||||||
|
False,
|
||||||
u''
|
u''
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1297,8 +1313,9 @@ class KOBOTOUCH(KOBO):
|
|||||||
OPT_SHOW_PREVIEWS = 7
|
OPT_SHOW_PREVIEWS = 7
|
||||||
OPT_SHOW_RECOMMENDATIONS = 8
|
OPT_SHOW_RECOMMENDATIONS = 8
|
||||||
OPT_UPDATE_SERIES_DETAILS = 9
|
OPT_UPDATE_SERIES_DETAILS = 9
|
||||||
OPT_SUPPORT_NEWER_FIRMWARE = 10
|
OPT_MODIFY_CSS = 10
|
||||||
OPT_DEBUGGING_TITLE = 11
|
OPT_SUPPORT_NEWER_FIRMWARE = 11
|
||||||
|
OPT_DEBUGGING_TITLE = 12
|
||||||
|
|
||||||
opts = None
|
opts = None
|
||||||
|
|
||||||
@ -1805,11 +1822,41 @@ class KOBOTOUCH(KOBO):
|
|||||||
debug_print("KoboTouch:imagefilename_from_imageID - no cover image found - ImageID=%s" % (ImageID))
|
debug_print("KoboTouch:imagefilename_from_imageID - no cover image found - ImageID=%s" % (ImageID))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_extra_css(self):
|
||||||
|
extra_sheet = None
|
||||||
|
|
||||||
|
if self.modifying_css():
|
||||||
|
extra_css_path = os.path.join(self._main_prefix, self.KOBO_EXTRA_CSSFILE)
|
||||||
|
if os.path.exists(extra_css_path):
|
||||||
|
from cssutils import parseFile as cssparseFile
|
||||||
|
try:
|
||||||
|
extra_sheet = cssparseFile(extra_css_path)
|
||||||
|
debug_print("KoboTouch:get_extra_css: Using extra CSS in {0} ({1} rules)".format(extra_css_path, len(extra_sheet.cssRules)))
|
||||||
|
except Exception as e:
|
||||||
|
debug_print("KoboTouch:get_extra_css: Problem parsing extra CSS file {0}".format(extra_css_path))
|
||||||
|
debug_print("KoboTouch:get_extra_css: Exception {0}".format(e))
|
||||||
|
return extra_sheet
|
||||||
|
|
||||||
|
|
||||||
def upload_books(self, files, names, on_card=None, end_session=True,
|
def upload_books(self, files, names, on_card=None, end_session=True,
|
||||||
metadata=None):
|
metadata=None):
|
||||||
debug_print('KoboTouch:upload_books - %d books'%(len(files)))
|
debug_print('KoboTouch:upload_books - %d books'%(len(files)))
|
||||||
debug_print('KoboTouch:upload_books - files=', files)
|
debug_print('KoboTouch:upload_books - files=', files)
|
||||||
|
|
||||||
|
if self.modifying_epub():
|
||||||
|
self.extra_sheet = self.get_extra_css()
|
||||||
|
i = 0
|
||||||
|
for file, n, mi in zip(files, names, metadata):
|
||||||
|
debug_print("KoboTouch:upload_books: Processing book: {0} by {1}".format(mi.title, " and ".join(mi.authors)))
|
||||||
|
debug_print("KoboTouch:upload_books: file=%s, name=%s" % (file, n))
|
||||||
|
self.report_progress(i / float(len(files)), "Processing book: {0} by {1}".format(mi.title, " and ".join(mi.authors)))
|
||||||
|
mi.kte_calibre_name = n
|
||||||
|
self._modify_epub(file, mi)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
self.report_progress(0, 'Working...')
|
||||||
|
|
||||||
result = super(KOBOTOUCH, self).upload_books(files, names, on_card, end_session, metadata)
|
result = super(KOBOTOUCH, self).upload_books(files, names, on_card, end_session, metadata)
|
||||||
# debug_print('KoboTouch:upload_books - result=', result)
|
# debug_print('KoboTouch:upload_books - result=', result)
|
||||||
|
|
||||||
@ -1848,6 +1895,65 @@ class KOBOTOUCH(KOBO):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _modify_epub(self, file, metadata, container=None):
|
||||||
|
debug_print("KoboTouch:_modify_epub:Processing {0} - {1}".format(metadata.author_sort, metadata.title))
|
||||||
|
|
||||||
|
# Currently only modifying CSS, so if no stylesheet, don't do anything
|
||||||
|
if not self.extra_sheet:
|
||||||
|
return True
|
||||||
|
|
||||||
|
commit_container = False
|
||||||
|
if not container:
|
||||||
|
commit_container = True
|
||||||
|
try:
|
||||||
|
from calibre.ebooks.oeb.polish.container import get_container
|
||||||
|
debug_print("KoboTouch:_modify_epub: creating container")
|
||||||
|
container = get_container(file)
|
||||||
|
container.css_preprocessor = DummyCSSPreProcessor()
|
||||||
|
except Exception as e:
|
||||||
|
debug_print("KoboTouch:_modify_epub: exception from get_container {0} - {1}".format(metadata.author_sort, metadata.title))
|
||||||
|
debug_print("KoboTouch:_modify_epub: exception is: {0}".format(e))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
debug_print("KoboTouch:_modify_epub: received container")
|
||||||
|
|
||||||
|
cssnames = [n for n in container.name_path_map if n.endswith('.css')]
|
||||||
|
for cssname in cssnames:
|
||||||
|
newsheet = container.parsed(cssname)
|
||||||
|
oldrules = len(newsheet.cssRules)
|
||||||
|
# remove any existing @page rules in epub css
|
||||||
|
# if css to be appended contains an @page rule
|
||||||
|
if self.extra_sheet and len([r for r in self.extra_sheet if r.type == r.PAGE_RULE]):
|
||||||
|
page_rules = [r for r in newsheet if r.type == r.PAGE_RULE]
|
||||||
|
if len(page_rules) > 0:
|
||||||
|
debug_print("KoboTouch:_modify_epub:Removing existing @page rules")
|
||||||
|
for rule in page_rules:
|
||||||
|
rule.style = ''
|
||||||
|
# remove any existing widow/orphan settings in epub css
|
||||||
|
# if css to be appended contains a widow/orphan rule or we there is no extra CSS file
|
||||||
|
if (len([r for r in self.extra_sheet if r.type == r.STYLE_RULE \
|
||||||
|
and (r.style['widows'] or r.style['orphans'])]) > 0):
|
||||||
|
widow_orphan_rules = [r for r in newsheet if r.type == r.STYLE_RULE \
|
||||||
|
and (r.style['widows'] or r.style['orphans'])]
|
||||||
|
if len(widow_orphan_rules) > 0:
|
||||||
|
debug_print("KoboTouch:_modify_epub:Removing existing widows/orphans attribs")
|
||||||
|
for rule in widow_orphan_rules:
|
||||||
|
rule.style.removeProperty('widows')
|
||||||
|
rule.style.removeProperty('orphans')
|
||||||
|
# append all rules from kobo extra css stylesheet
|
||||||
|
for addrule in [r for r in self.extra_sheet.cssRules]:
|
||||||
|
newsheet.insertRule(addrule, len(newsheet.cssRules))
|
||||||
|
debug_print("KoboTouch:_modify_epub:CSS rules {0} -> {1} ({2})".format(oldrules, len(newsheet.cssRules), cssname))
|
||||||
|
container.dirty(cssname)
|
||||||
|
|
||||||
|
if commit_container:
|
||||||
|
debug_print("KoboTouch:_modify_epub: committing container.")
|
||||||
|
os.unlink(file)
|
||||||
|
container.commit(file)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def delete_via_sql(self, ContentID, ContentType):
|
def delete_via_sql(self, ContentID, ContentType):
|
||||||
imageId = super(KOBOTOUCH, self).delete_via_sql(ContentID, ContentType)
|
imageId = super(KOBOTOUCH, self).delete_via_sql(ContentID, ContentType)
|
||||||
|
|
||||||
@ -1872,11 +1978,11 @@ class KOBOTOUCH(KOBO):
|
|||||||
cursor.execute('delete from content where BookID is Null and ContentID =?',t)
|
cursor.execute('delete from content where BookID is Null and ContentID =?',t)
|
||||||
|
|
||||||
# Remove the content_settings entry
|
# Remove the content_settings entry
|
||||||
debug_print('KoboTouch:delete_via_sql: detete from content_settings')
|
debug_print('KoboTouch:delete_via_sql: delete from content_settings')
|
||||||
cursor.execute('delete from content_settings where ContentID =?',t)
|
cursor.execute('delete from content_settings where ContentID =?',t)
|
||||||
|
|
||||||
# Remove the ratings entry
|
# Remove the ratings entry
|
||||||
debug_print('KoboTouch:delete_via_sql: detete from ratings')
|
debug_print('KoboTouch:delete_via_sql: delete from ratings')
|
||||||
cursor.execute('delete from ratings where ContentID =?',t)
|
cursor.execute('delete from ratings where ContentID =?',t)
|
||||||
|
|
||||||
# Remove any entries for the Activity table - removes tile from new home page
|
# Remove any entries for the Activity table - removes tile from new home page
|
||||||
@ -2636,6 +2742,13 @@ class KOBOTOUCH(KOBO):
|
|||||||
opts = self.settings()
|
opts = self.settings()
|
||||||
return opts.extra_customization[self.OPT_KEEP_COVER_ASPECT_RATIO]
|
return opts.extra_customization[self.OPT_KEEP_COVER_ASPECT_RATIO]
|
||||||
|
|
||||||
|
def modifying_epub(self):
|
||||||
|
return self.modifying_css()
|
||||||
|
|
||||||
|
def modifying_css(self):
|
||||||
|
opts = self.settings()
|
||||||
|
return opts.extra_customization[self.OPT_MODIFY_CSS]
|
||||||
|
|
||||||
|
|
||||||
def supports_bookshelves(self):
|
def supports_bookshelves(self):
|
||||||
return self.dbversion >= self.min_supported_dbversion
|
return self.dbversion >= self.min_supported_dbversion
|
||||||
|
Loading…
x
Reference in New Issue
Block a user