mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Tests for resolving properties
This commit is contained in:
parent
176b6b248d
commit
0d77ec1451
@ -93,6 +93,7 @@ def iterdeclaration(decl):
|
|||||||
else:
|
else:
|
||||||
for k, v in n(p.name, p.propertyValue).iteritems():
|
for k, v in n(p.name, p.propertyValue).iteritems():
|
||||||
yield Property(k, v, p.literalpriority)
|
yield Property(k, v, p.literalpriority)
|
||||||
|
|
||||||
class Values(tuple):
|
class Values(tuple):
|
||||||
|
|
||||||
''' A tuple of `cssutils.css.Value ` (and its subclasses) objects. Also has a
|
''' A tuple of `cssutils.css.Value ` (and its subclasses) objects. Also has a
|
||||||
@ -105,6 +106,12 @@ class Values(tuple):
|
|||||||
ans.is_important = priority == 'important'
|
ans.is_important = priority == 'important'
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cssText(self):
|
||||||
|
if len(self) == 1:
|
||||||
|
return self[0].cssText
|
||||||
|
return tuple(x.cssText for x in self)
|
||||||
|
|
||||||
def normalize_style_declaration(decl, sheet_name):
|
def normalize_style_declaration(decl, sheet_name):
|
||||||
ans = {}
|
ans = {}
|
||||||
for prop in iterdeclaration(decl):
|
for prop in iterdeclaration(decl):
|
||||||
@ -129,16 +136,16 @@ def resolve_declarations(decls):
|
|||||||
ans[name] = first_val
|
ans[name] = first_val
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def resolve_styles(container, name):
|
def resolve_styles(container, name, select=None):
|
||||||
root = container.parsed(name)
|
root = container.parsed(name)
|
||||||
select = Select(root, ignore_inappropriate_pseudo_classes=True)
|
select = select or Select(root, ignore_inappropriate_pseudo_classes=True)
|
||||||
style_map = defaultdict(list)
|
style_map = defaultdict(list)
|
||||||
pseudo_style_map = defaultdict(list)
|
pseudo_style_map = defaultdict(list)
|
||||||
rule_index_counter = count()
|
rule_index_counter = count()
|
||||||
pseudo_pat = re.compile(ur':{1,2}(%s)' % ('|'.join(INAPPROPRIATE_PSEUDO_CLASSES)), re.I)
|
pseudo_pat = re.compile(ur':{1,2}(%s)' % ('|'.join(INAPPROPRIATE_PSEUDO_CLASSES)), re.I)
|
||||||
|
|
||||||
def process_sheet(sheet, sheet_name):
|
def process_sheet(sheet, sheet_name):
|
||||||
for rule, sheet_name, rule_index in iterrules(container, sheet_name, rules=sheet, rule_index_counter=rule_index_counter, rule_type=CSSRule.STYLE_RULE):
|
for rule, sheet_name, rule_index in iterrules(container, sheet_name, rules=sheet, rule_index_counter=rule_index_counter, rule_type='STYLE_RULE'):
|
||||||
for selector in rule.selectorList:
|
for selector in rule.selectorList:
|
||||||
text = selector.selectorText
|
text = selector.selectorText
|
||||||
try:
|
try:
|
||||||
@ -155,7 +162,7 @@ def resolve_styles(container, name):
|
|||||||
for elem in matches:
|
for elem in matches:
|
||||||
pseudo_style_map[elem].append(StyleDeclaration(specificity(rule_index, selector), style, m.group(1)))
|
pseudo_style_map[elem].append(StyleDeclaration(specificity(rule_index, selector), style, m.group(1)))
|
||||||
|
|
||||||
process_sheet(html_css_stylesheet(), 'user-agent.css')
|
process_sheet(html_css_stylesheet(container), 'user-agent.css')
|
||||||
|
|
||||||
for elem in root.iterdescendants(XHTML('style'), XHTML('link')):
|
for elem in root.iterdescendants(XHTML('style'), XHTML('link')):
|
||||||
if elem.tag.lower().endswith('style'):
|
if elem.tag.lower().endswith('style'):
|
||||||
@ -192,7 +199,7 @@ def resolve_styles(container, name):
|
|||||||
style_map = {elem:resolve_declarations(x) for elem, x in style_map.iteritems()}
|
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_declarations(x) for elem, x in pseudo_style_map.iteritems()}
|
||||||
|
|
||||||
return style_map, pseudo_style_map
|
return style_map, pseudo_style_map, select
|
||||||
|
|
||||||
_defvals = None
|
_defvals = None
|
||||||
|
|
||||||
@ -203,7 +210,7 @@ def defvals():
|
|||||||
_defvals = {k:Values(Property(k, u(val)).propertyValue) for k, val in DEFAULTS.iteritems()}
|
_defvals = {k:Values(Property(k, u(val)).propertyValue) for k, val in DEFAULTS.iteritems()}
|
||||||
return _defvals
|
return _defvals
|
||||||
|
|
||||||
def get_resolved_property(elem, name, style_map):
|
def resolve_property(elem, name, style_map):
|
||||||
''' Given a `style_map` previously generated by :func:`resolve_styles()` and
|
''' Given a `style_map` previously generated by :func:`resolve_styles()` and
|
||||||
a property `name`, returns the effective value of that property for the
|
a property `name`, returns the effective value of that property for the
|
||||||
specified element. Handles inheritance and CSS cascading rules. Returns
|
specified element. Handles inheritance and CSS cascading rules. Returns
|
||||||
@ -212,11 +219,11 @@ def get_resolved_property(elem, name, style_map):
|
|||||||
|
|
||||||
inheritable = name in INHERITED
|
inheritable = name in INHERITED
|
||||||
q = elem
|
q = elem
|
||||||
while q:
|
while q is not None:
|
||||||
s = style_map.get(q)
|
s = style_map.get(q)
|
||||||
if s is not None:
|
if s is not None:
|
||||||
val = s.get(name)
|
val = s.get(name)
|
||||||
if val is not None:
|
if val is not None:
|
||||||
return val
|
return val
|
||||||
q = elem.getparent() if inheritable else None
|
q = q.getparent() if inheritable else None
|
||||||
return defvals().get(name)
|
return defvals().get(name)
|
||||||
|
@ -4,12 +4,13 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
print_function)
|
print_function)
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2016, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from calibre.constants import iswindows
|
from calibre.constants import iswindows
|
||||||
|
|
||||||
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
from calibre.ebooks.oeb.base import OEB_STYLES, OEB_DOCS
|
||||||
from calibre.ebooks.oeb.polish.cascade import iterrules
|
from calibre.ebooks.oeb.polish.cascade import iterrules, resolve_property, resolve_styles, DEFAULTS
|
||||||
from calibre.ebooks.oeb.polish.container import ContainerBase, href_to_name
|
from calibre.ebooks.oeb.polish.container import ContainerBase, href_to_name
|
||||||
from calibre.ebooks.oeb.polish.tests.base import BaseTest
|
from calibre.ebooks.oeb.polish.tests.base import BaseTest
|
||||||
from calibre.utils.logging import Log, Stream
|
from calibre.utils.logging import Log, Stream
|
||||||
@ -61,3 +62,31 @@ class CascadeTest(BaseTest):
|
|||||||
get_rules({'x/one.css':'@media xyz { body { color: red; } }'}, l=0)
|
get_rules({'x/one.css':'@media xyz { body { color: red; } }'}, l=0)
|
||||||
c = get_rules({'x/one.css':'@import "../two.css";', 'two.css':'@import "x/one.css"; body { color: red; }'})[1]
|
c = get_rules({'x/one.css':'@import "../two.css";', 'two.css':'@import "x/one.css"; body { color: red; }'})[1]
|
||||||
self.assertIn('Recursive import', c.log_stream.getvalue().decode('utf-8'))
|
self.assertIn('Recursive import', c.log_stream.getvalue().decode('utf-8'))
|
||||||
|
|
||||||
|
def test_resolve_styles(self):
|
||||||
|
|
||||||
|
def test_property(select, style_map, selector, name, val=None):
|
||||||
|
elem = next(select(selector))
|
||||||
|
ans = resolve_property(elem, name, style_map)
|
||||||
|
if val is None:
|
||||||
|
val = type('')(DEFAULTS[name])
|
||||||
|
self.assertEqual(val, ans.cssText)
|
||||||
|
|
||||||
|
def get_maps(html, styles=None):
|
||||||
|
html = '<html><head><link href="styles.css"></head><body>{}</body></html>'.format(html)
|
||||||
|
c = VirtualContainer({'index.html':html, 'styles.css':styles or 'body { color: red }'})
|
||||||
|
style_map, pseudo_style_map, select = resolve_styles(c, 'index.html')
|
||||||
|
tp = partial(test_property, select, style_map)
|
||||||
|
return tp
|
||||||
|
|
||||||
|
t = get_maps('<p style="margin:11pt"><b>x</b>xx</p>')
|
||||||
|
t('body', 'color', 'red')
|
||||||
|
t('p', 'color', 'red')
|
||||||
|
t('b', 'font-weight', 'bold')
|
||||||
|
t('p', 'margin-top', '11pt')
|
||||||
|
t('b', 'margin-top')
|
||||||
|
t('body', 'display', 'block')
|
||||||
|
t('b', 'display', 'inline')
|
||||||
|
for e in ('body', 'p', 'b'):
|
||||||
|
for prop in 'background-color text-indent'.split():
|
||||||
|
t(e, prop)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user