From adbd85bf33f918f866830ad60c6cb627537fc14d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 13 Jun 2019 09:03:17 +0530 Subject: [PATCH] News downloads: Fix incorrect font sizes for some text in the index pages. Fixes #1832628 [Calibre viewer freezes when opening news download converted to LRF](https://bugs.launchpad.net/calibre/+bug/1832628) Conversion now uses a dedicated attribute data-calibre-rescale for dynamic font sizing rather than relying on class names which can get mangled by CSS flattening --- src/calibre/ebooks/oeb/transforms/flatcss.py | 24 ++--- src/calibre/ebooks/oeb/transforms/jacket.py | 4 +- src/calibre/web/feeds/templates.py | 94 ++++++++++---------- 3 files changed, 57 insertions(+), 65 deletions(-) diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index ebe07e16f4..4b777dbc04 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -33,21 +33,6 @@ def asfloat(value, default): return float(value) -def dynamic_rescale_factor(node): - classes = node.get('class', '').split(' ') - classes = [x.replace('calibre_rescale_', '') for x in classes if - x.startswith('calibre_rescale_')] - if not classes: - return None - factor = 1.0 - for x in classes: - try: - factor *= float(x)/100. - except ValueError: - continue - return factor - - class KeyMapper(object): def __init__(self, sbase, dbase, dkey): @@ -470,8 +455,12 @@ class CSSFlattener(object): if not self.context.disable_font_rescaling and not is_drop_cap: _sbase = self.sbase if self.sbase is not None else \ self.context.source.fbase - dyn_rescale = dynamic_rescale_factor(node) + dyn_rescale = node.attrib.pop('data-calibre-rescale', None) if dyn_rescale is not None: + try: + dyn_rescale = float(dyn_rescale) / 100.0 + except Exception: + dyn_rescale = 1 fsize = self.fmap[_sbase] fsize *= dyn_rescale cssdict['font-size'] = '%0.5fem'%(fsize/psize) @@ -541,10 +530,11 @@ class CSSFlattener(object): items = sorted(iteritems(cssdict)) css = u';\n'.join(u'%s: %s' % (key, val) for key, val in items) classes = node.get('class', '').strip() or 'calibre' + classes_list = classes.split() # lower() because otherwise if the document uses the same class # name with different case, both cases will apply, leading # to incorrect results. - klass = ascii_text(STRIPNUM.sub('', classes.split()[0])).lower().strip().replace(' ', '_') + klass = ascii_text(STRIPNUM.sub('', classes_list[0])).lower().strip().replace(' ', '_') if css in styles: match = styles[css] else: diff --git a/src/calibre/ebooks/oeb/transforms/jacket.py b/src/calibre/ebooks/oeb/transforms/jacket.py index 23abb83193..68ae89b8ba 100644 --- a/src/calibre/ebooks/oeb/transforms/jacket.py +++ b/src/calibre/ebooks/oeb/transforms/jacket.py @@ -369,11 +369,11 @@ def render_jacket(mi, output_profile, # the same as for text in the main book. So text with size x em will # be rescaled to the same value in both the jacket and the main content. # - # We cannot use calibre_rescale_100 on the body tag as that will just + # We cannot use data-calibre-rescale 100 on the body tag as that will just # give the body tag a font size of 1em, which is useless. for body in root.xpath('//*[local-name()="body"]'): fw = body.makeelement(XHTML('div')) - fw.set('class', 'calibre_rescale_100') + fw.set('data-calibre-rescale', '100') for child in body: fw.append(child) body.append(fw) diff --git a/src/calibre/web/feeds/templates.py b/src/calibre/web/feeds/templates.py index e3c05239d4..a8c05fc614 100644 --- a/src/calibre/web/feeds/templates.py +++ b/src/calibre/web/feeds/templates.py @@ -16,9 +16,13 @@ from calibre import strftime, isbytestring from polyglot.builtins import unicode_type -def CLASS(*args, **kwargs): # class is a reserved word in Python - kwargs['class'] = ' '.join(args) - return kwargs +def attrs(*args, **kw): + rescale = kw.pop('rescale', None) + if rescale is not None: + kw['data-calibre-rescale'] = unicode_type(rescale) + if args: + kw['class'] = ' '.join(args) + return kw # Regular templates @@ -96,17 +100,17 @@ class IndexTemplate(Template): head.append(STYLE(style, type='text/css')) if extra_css: head.append(STYLE(extra_css, type='text/css')) - ul = UL(CLASS('calibre_feed_list')) + ul = UL(attrs('calibre_feed_list')) for i, feed in enumerate(feeds): if feed: - li = LI(A(feed.title, CLASS('feed', 'calibre_rescale_120', + li = LI(A(feed.title, attrs('feed', rescale=120, href='feed_%d/index.html'%i)), id='feed_%d'%i) ul.append(li) div = DIV( PT(IMG(src=masthead,alt="masthead"),style='text-align:center'), PT(date, style='text-align:right'), ul, - CLASS('calibre_rescale_100')) + attrs(rescale=100)) self.root = HTML(head, BODY(div)) if self.html_lang: self.root.set('lang', self.html_lang) @@ -117,7 +121,7 @@ class FeedTemplate(Template): def get_navbar(self, f, feeds, top=True): if len(feeds) < 2: return DIV() - navbar = DIV('| ', CLASS('calibre_navbar', 'calibre_rescale_70', + navbar = DIV('| ', attrs('calibre_navbar', rescale=70, style='text-align:center')) if not top: hr = HR() @@ -153,8 +157,8 @@ class FeedTemplate(Template): div = DIV( H2(feed.title, - CLASS('calibre_feed_title', 'calibre_rescale_160')), - CLASS('calibre_rescale_100') + attrs('calibre_feed_title', rescale=160)), + attrs(rescale=100) ) body.append(div) if getattr(feed, 'image', None): @@ -162,26 +166,25 @@ class FeedTemplate(Template): alt=feed.image_alt if feed.image_alt else '', src=feed.image_url ), - CLASS('calibre_feed_image'))) + attrs('calibre_feed_image'))) if getattr(feed, 'description', None): - d = DIV(clean_xml_chars(feed.description), CLASS('calibre_feed_description', - 'calibre_rescale_80')) + d = DIV(clean_xml_chars(feed.description), attrs('calibre_feed_description', rescale=80)) d.append(BR()) div.append(d) - ul = UL(CLASS('calibre_article_list')) + ul = UL(attrs('calibre_article_list')) for i, article in enumerate(feed.articles): if not getattr(article, 'downloaded', False): continue li = LI( - A(article.title, CLASS('article calibre_rescale_120', + A(article.title, attrs('article', rescale=120, href=article.url)), - SPAN(article.formatted_date, CLASS('article_date')), - CLASS('calibre_rescale_100', id='article_%d'%i, + SPAN(article.formatted_date, attrs('article_date')), + attrs(rescale=100, id='article_%d'%i, style='padding-bottom:0.5em') ) if article.summary: li.append(DIV(clean_xml_chars(cutoff(article.text_summary)), - CLASS('article_description', 'calibre_rescale_70'))) + attrs('article_description', rescale=70))) ul.append(li) div.append(ul) div.append(self.get_navbar(f, feeds, top=False)) @@ -205,7 +208,7 @@ class NavBarTemplate(Template): prefix += '/' align = 'center' if center else 'left' - navbar = DIV(CLASS('calibre_navbar', 'calibre_rescale_70', + navbar = DIV(attrs('calibre_navbar', rescale=70, style='text-align:'+align)) if bottom: if not url.startswith('file://'): @@ -260,17 +263,17 @@ class TouchscreenIndexTemplate(Template): if extra_css: head.append(STYLE(extra_css, type='text/css')) - toc = TABLE(CLASS('toc'),width="100%",border="0",cellpadding="3px") + toc = TABLE(attrs('toc'),width="100%",border="0",cellpadding="3px") for i, feed in enumerate(feeds): if feed: tr = TR() - tr.append(TD(CLASS('calibre_rescale_120'), A(feed.title, href='feed_%d/index.html'%i))) + tr.append(TD(attrs(rescale=120), A(feed.title, href='feed_%d/index.html'%i))) tr.append(TD('%s' % len(feed.articles), style="text-align:right")) toc.append(tr) div = DIV( masthead_p, - H3(CLASS('publish_date'),date), - DIV(CLASS('divider')), + H3(attrs('publish_date'),date), + DIV(attrs('divider')), toc) self.root = HTML(head, BODY(div)) if self.html_lang: @@ -303,28 +306,28 @@ class TouchscreenFeedTemplate(Template): feed = feeds[f] # Construct the navbar - navbar_t = TABLE(CLASS('touchscreen_navbar')) + navbar_t = TABLE(attrs('touchscreen_navbar')) navbar_tr = TR() # Previous Section link = '' if f > 0: - link = A(CLASS('feed_link'), + link = A(attrs('feed_link'), trim_title(feeds[f-1].title), href='../feed_%d/index.html' % int(f-1)) - navbar_tr.append(TD(CLASS('feed_prev'),link)) + navbar_tr.append(TD(attrs('feed_prev'),link)) # Up to Sections link = A(_('Sections'), href="../index.html") - navbar_tr.append(TD(CLASS('feed_up'),link)) + navbar_tr.append(TD(attrs('feed_up'),link)) # Next Section link = '' if f < len(feeds)-1: - link = A(CLASS('feed_link'), + link = A(attrs('feed_link'), trim_title(feeds[f+1].title), href='../feed_%d/index.html' % int(f+1)) - navbar_tr.append(TD(CLASS('feed_next'),link)) + navbar_tr.append(TD(attrs('feed_next'),link)) navbar_t.append(navbar_tr) top_navbar = navbar_t bottom_navbar = copy.copy(navbar_t) @@ -339,7 +342,7 @@ class TouchscreenFeedTemplate(Template): body = BODY() div = DIV( top_navbar, - H2(feed.title, CLASS('feed_title')) + H2(feed.title, attrs('feed_title')) ) body.append(div) @@ -348,10 +351,9 @@ class TouchscreenFeedTemplate(Template): alt=feed.image_alt if feed.image_alt else '', src=feed.image_url ), - CLASS('calibre_feed_image'))) + attrs('calibre_feed_image'))) if getattr(feed, 'description', None): - d = DIV(clean_xml_chars(feed.description), CLASS('calibre_feed_description', - 'calibre_rescale_80')) + d = DIV(clean_xml_chars(feed.description), attrs('calibre_feed_description', rescale=80)) d.append(BR()) div.append(d) @@ -359,15 +361,15 @@ class TouchscreenFeedTemplate(Template): if not getattr(article, 'downloaded', False): continue - div_td = DIV(CLASS('article_summary'), - A(article.title, CLASS('summary_headline','calibre_rescale_120', + div_td = DIV(attrs('article_summary'), + A(article.title, attrs('summary_headline',rescale=120, href=article.url))) if article.author: div_td.append(DIV(article.author, - CLASS('summary_byline', 'calibre_rescale_100'))) + attrs('summary_byline', rescale=100))) if article.summary: div_td.append(DIV(cutoff(article.text_summary), - CLASS('summary_text', 'calibre_rescale_100'))) + attrs('summary_text', rescale=100))) div.append(div_td) div.append(bottom_navbar) @@ -388,7 +390,7 @@ class TouchscreenNavBarTemplate(Template): head.append(STYLE(extra_css, type='text/css')) navbar = DIV() - navbar_t = TABLE(CLASS('touchscreen_navbar')) + navbar_t = TABLE(attrs('touchscreen_navbar')) navbar_tr = TR() if bottom and not url.startswith('file://'): @@ -401,25 +403,25 @@ class TouchscreenNavBarTemplate(Template): navbar.append(BR()) # | Previous if art > 0: - link = A(CLASS('article_link'),_('Previous'),href='%s../article_%d/index.html'%(prefix, art-1)) - navbar_tr.append(TD(CLASS('article_prev'),link)) + link = A(attrs('article_link'),_('Previous'),href='%s../article_%d/index.html'%(prefix, art-1)) + navbar_tr.append(TD(attrs('article_prev'),link)) else: - navbar_tr.append(TD(CLASS('article_prev'),'')) + navbar_tr.append(TD(attrs('article_prev'),'')) # | Articles | Sections | - link = A(CLASS('articles_link'),_('Articles'), href='%s../index.html#article_%d'%(prefix, art)) - navbar_tr.append(TD(CLASS('article_articles_list'),link)) + link = A(attrs('articles_link'),_('Articles'), href='%s../index.html#article_%d'%(prefix, art)) + navbar_tr.append(TD(attrs('article_articles_list'),link)) - link = A(CLASS('sections_link'),_('Sections'), href='%s../../index.html#feed_%d'%(prefix, feed)) - navbar_tr.append(TD(CLASS('article_sections_list'),link)) + link = A(attrs('sections_link'),_('Sections'), href='%s../../index.html#feed_%d'%(prefix, feed)) + navbar_tr.append(TD(attrs('article_sections_list'),link)) # | Next next_art = 'feed_%d'%(feed+1) if art == number_of_articles_in_feed - 1 \ else 'article_%d'%(art+1) up = '../..' if art == number_of_articles_in_feed - 1 else '..' - link = A(CLASS('article_link'), _('Next'), href='%s%s/%s/index.html'%(prefix, up, next_art)) - navbar_tr.append(TD(CLASS('article_next'),link)) + link = A(attrs('article_link'), _('Next'), href='%s%s/%s/index.html'%(prefix, up, next_art)) + navbar_tr.append(TD(attrs('article_next'),link)) navbar_t.append(navbar_tr) navbar.append(navbar_t) # print "\n%s\n" % etree.tostring(navbar, pretty_print=True)