Conversion pipeline: Add support for !important when flattening CSS rules. Fixes #1987011 [CSS Lost, epub->azw3](https://bugs.launchpad.net/calibre/+bug/1987011)

This commit is contained in:
Kovid Goyal 2022-08-29 06:34:56 +05:30
parent 4b1ab3e036
commit cfe219018d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -100,6 +100,13 @@ def test_media_ok():
assert not media_ok('screen and (device-width:10px)') assert not media_ok('screen and (device-width:10px)')
class style_map(dict):
def __init__(self):
super().__init__()
self.important_properties = set()
class StylizerRules: class StylizerRules:
def __init__(self, opts, profile, stylesheets): def __init__(self, opts, profile, stylesheets):
@ -144,16 +151,24 @@ class StylizerRules:
return results return results
def flatten_style(self, cssstyle): def flatten_style(self, cssstyle):
style = {} style = style_map()
for prop in cssstyle: for prop in cssstyle:
name = prop.name name = prop.name
normalizer = normalizers.get(name, None) normalizer = normalizers.get(name, None)
is_important = prop.priority == 'important'
if normalizer is not None: if normalizer is not None:
style.update(normalizer(name, prop.propertyValue)) for name, val in normalizer(name, prop.propertyValue).items():
style[name] = val
if is_important:
style.important_properties.add(name)
elif name == 'text-align': elif name == 'text-align':
style['text-align'] = self._apply_text_align(prop.value) style['text-align'] = self._apply_text_align(prop.value)
if is_important:
style.important_properties.add(name)
else: else:
style[name] = prop.value style[name] = prop.value
if is_important:
style.important_properties.add(name)
if 'font-size' in style: if 'font-size' in style:
size = style['font-size'] size = style['font-size']
if size == 'normal': if size == 'normal':
@ -404,6 +419,9 @@ class Stylizer:
return '\n'.join(rules) return '\n'.join(rules)
no_important_properties = frozenset()
class Style: class Style:
MS_PAT = re.compile(r'^\s*(mso-|panose-|text-underline|tab-interval)') MS_PAT = re.compile(r'^\s*(mso-|panose-|text-underline|tab-interval)')
@ -411,7 +429,7 @@ class Style:
self._element = element self._element = element
self._profile = stylizer.profile self._profile = stylizer.profile
self._stylizer = stylizer self._stylizer = stylizer
self._style = {} self._style = style_map()
self._fontSize = None self._fontSize = None
self._width = None self._width = None
self._height = None self._height = None
@ -428,7 +446,25 @@ class Style:
return self._style.pop(prop, default) return self._style.pop(prop, default)
def _update_cssdict(self, cssdict): def _update_cssdict(self, cssdict):
self._style.update(cssdict) self._update_style(cssdict)
def _update_style(self, cssdict):
current_ip = getattr(self._style, 'important_properties', no_important_properties)
if current_ip is no_important_properties:
s = style_map()
s.update(self._style)
self._style = s
current_ip = self._style.important_properties
update_ip = getattr(cssdict, 'important_properties', no_important_properties)
for name, val in cssdict.items():
override = False
if name in update_ip:
current_ip.add(name)
override = True
elif name not in current_ip:
override = True
if override:
self._style[name] = val
def _update_pseudo_class(self, name, cssdict): def _update_pseudo_class(self, name, cssdict):
orig = self._pseudo_classes.get(name, {}) orig = self._pseudo_classes.get(name, {})
@ -450,7 +486,7 @@ class Style:
return return
if url_replacer is not None: if url_replacer is not None:
replaceUrls(style, url_replacer, ignoreImportRules=True) replaceUrls(style, url_replacer, ignoreImportRules=True)
self._style.update(self._stylizer.flatten_style(style)) self._update_style(self._stylizer.flatten_style(style))
def _has_parent(self): def _has_parent(self):
try: try: