From a853e4dac0f1553311f05dd8413d780f662c2d7f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 Apr 2016 14:51:24 +0530 Subject: [PATCH] Function to resolve pseudo-element properties --- src/calibre/ebooks/oeb/polish/cascade.py | 18 ++++++++++++- .../ebooks/oeb/polish/tests/cascade.py | 25 ++++++++++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/cascade.py b/src/calibre/ebooks/oeb/polish/cascade.py index 58169a6c52..0d6fa90713 100644 --- a/src/calibre/ebooks/oeb/polish/cascade.py +++ b/src/calibre/ebooks/oeb/polish/cascade.py @@ -137,6 +137,12 @@ def resolve_declarations(decls): ans[name] = first_val return ans +def resolve_pseudo_declarations(decls): + groups = defaultdict(list) + for d in decls: + groups[d.pseudo_element].append(d) + return {k:resolve_declarations(v) for k, v in groups.iteritems()} + def resolve_styles(container, name, select=None, sheet_callback=None): root = container.parsed(name) select = select or Select(root, ignore_inappropriate_pseudo_classes=True) @@ -200,7 +206,7 @@ def resolve_styles(container, name, select=None, sheet_callback=None): x.sort(key=itemgetter(0), reverse=True) style_map = {elem:resolve_declarations(x) for elem, x in style_map.iteritems()} - pseudo_style_map = {elem:resolve_declarations(x) for elem, x in pseudo_style_map.iteritems()} + pseudo_style_map = {elem:resolve_pseudo_declarations(x) for elem, x in pseudo_style_map.iteritems()} return style_map, pseudo_style_map, select @@ -230,3 +236,13 @@ def resolve_property(elem, name, style_map): return val q = q.getparent() if inheritable else None return defvals().get(name) + +def resolve_pseudo_property(elem, prop, name, pseudo_style_map): + style_map = pseudo_style_map.get(elem) + if style_map is not None: + prop_map = style_map.get(prop) + if prop_map is not None: + val = prop_map.get(name) + if val is not None: + return val + return defvals().get(name) diff --git a/src/calibre/ebooks/oeb/polish/tests/cascade.py b/src/calibre/ebooks/oeb/polish/tests/cascade.py index 25a4e7bf8d..21924e4713 100644 --- a/src/calibre/ebooks/oeb/polish/tests/cascade.py +++ b/src/calibre/ebooks/oeb/polish/tests/cascade.py @@ -10,7 +10,7 @@ from functools import partial from calibre.constants import iswindows from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS -from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_property, resolve_styles, DEFAULTS +from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_property, resolve_styles, DEFAULTS, resolve_pseudo_property from calibre.ebooks.oeb.polish.container import ContainerBase, href_to_name from calibre.ebooks.oeb.polish.tests.base import BaseTest from calibre.utils.logging import Log, Stream @@ -72,11 +72,21 @@ class CascadeTest(BaseTest): val = type('')(DEFAULTS[name]) self.assertEqual(val, ans.cssText) - def get_maps(html, styles=None): + def test_pseudo_property(select, pseudo_style_map, selector, prop, name, val=None): + elem = next(select(selector)) + ans = resolve_pseudo_property(elem, prop, name, pseudo_style_map) + if val is None: + val = type('')(DEFAULTS[name]) + self.assertEqual(val, ans.cssText) + + def get_maps(html, styles=None, pseudo=False): html = '{}'.format(html) c = VirtualContainer({'index.html':html, 'styles.css':styles or 'body { color: red; font-family: "Kovid Goyal", sans-serif }'}) style_map, pseudo_style_map, select = resolve_styles(c, 'index.html') - tp = partial(test_property, select, style_map) + if pseudo: + tp = partial(test_pseudo_property, select, pseudo_style_map) + else: + tp = partial(test_property, select, style_map) return tp t = get_maps('

xxx

') @@ -104,3 +114,12 @@ class CascadeTest(BaseTest): t('p', 'color', 'blue') t = get_maps('

xxx

', 'p {color: red; margin:11pt}') t('p', 'margin-top', '11pt') + t = get_maps('

', 'p:before { content: "xxx" }', True) + t('p', 'before', 'content', '"xxx"') + t = get_maps('

', 'body p:before { content: "xxx" } p:before { content: "yyy" }', True) + t('p', 'before', 'content', '"xxx"') + t = get_maps('

', "p:before { content: 'xxx' } p:first-letter { font-weight: bold }", True) + t('p', 'before', 'content', '"xxx"') + t('p', 'first-letter', 'font-weight', 'bold') + t = get_maps('

', 'p:before { content: xxx }', True) + t('p', 'before', 'content', 'xxx')