diff --git a/src/calibre/ebooks/oeb/polish/cascade.py b/src/calibre/ebooks/oeb/polish/cascade.py index 07b738016d..e425520760 100644 --- a/src/calibre/ebooks/oeb/polish/cascade.py +++ b/src/calibre/ebooks/oeb/polish/cascade.py @@ -253,7 +253,17 @@ def resolve_property(style_map, elem, name): return defvals().get(name) -def resolve_pseudo_property(style_map, pseudo_style_map, elem, prop, name, abort_on_missing=False): +def resolve_pseudo_property(style_map, pseudo_style_map, elem, prop, name, abort_on_missing=False, check_if_pseudo_applies=False, check_ancestors=False): + if check_if_pseudo_applies: + q = elem + while q is not None: + val = pseudo_style_map.get(q, {}).get(prop, {}).get(name) + if val is not None: + return True + if not check_ancestors: + break + q = q.getparent() + return False sub_map = pseudo_style_map.get(elem) if abort_on_missing and sub_map is None: return None @@ -266,5 +276,14 @@ def resolve_pseudo_property(style_map, pseudo_style_map, elem, prop, name, abort if val is not None: return val if name in INHERITED: + if check_ancestors: + q = elem.getparent() + while q is not None: + val = pseudo_style_map.get(q, {}).get(prop, {}).get(name) + if val is not None: + return val + if not check_ancestors: + break + q = q.getparent() return resolve_property(style_map, elem, name) return defvals().get(name) diff --git a/src/calibre/ebooks/oeb/polish/stats.py b/src/calibre/ebooks/oeb/polish/stats.py index 18fd558a98..67feeb3b65 100644 --- a/src/calibre/ebooks/oeb/polish/stats.py +++ b/src/calibre/ebooks/oeb/polish/stats.py @@ -249,8 +249,7 @@ class StatsCollector: update_usage_for_embed(font, chars) for rule in get_matching_rules(font_face_rules, font): self.font_stats[rule['src']] |= chars - q = resolve_pseudo_property(elem, 'first-letter', 'font-family', abort_on_missing=True) - if q is not None: + if resolve_pseudo_property(elem, 'first-letter', 'font-family', check_if_pseudo_applies=True): font = get_font_dict(elem, resolve_pseudo_property, pseudo='first-letter') text = get_element_text(elem, resolve_property, resolve_pseudo_property, self.capitalize_pat, for_pseudo='first-letter') m = self.first_letter_pat.search(text.lstrip()) @@ -259,9 +258,8 @@ class StatsCollector: update_usage_for_embed(font, chars) for rule in get_matching_rules(font_face_rules, font): self.font_stats[rule['src']] |= chars - q = resolve_pseudo_property(elem, 'first-line', 'font-family', abort_on_missing=True) - if q is not None: - font = get_font_dict(elem, resolve_pseudo_property, pseudo='first-line') + if resolve_pseudo_property(elem, 'first-line', 'font-family', check_if_pseudo_applies=True, check_ancestors=True): + font = get_font_dict(elem, partial(resolve_pseudo_property, check_ancestors=True), pseudo='first-line') text = get_element_text(elem, resolve_property, resolve_pseudo_property, self.capitalize_pat, for_pseudo='first-line') chars = frozenset(ord_string(text)) - exclude_chars update_usage_for_embed(font, chars) diff --git a/src/calibre/ebooks/oeb/polish/tests/cascade.py b/src/calibre/ebooks/oeb/polish/tests/cascade.py index 5372bd9c79..fd2e07c104 100644 --- a/src/calibre/ebooks/oeb/polish/tests/cascade.py +++ b/src/calibre/ebooks/oeb/polish/tests/cascade.py @@ -160,7 +160,6 @@ class CascadeTest(BaseTest): v = 'url(%s)' % v styles.append(f'{k} : {v};') styles.append('}\n') - html = f'{html}' files['styles.css'] = embeds + '\n'.join(styles) c = VirtualContainer(files) return StatsCollector(c, do_embed=True) @@ -207,6 +206,9 @@ class CascadeTest(BaseTest): s = get_stats('

abc

d\nef') self.assertEqual(s.font_stats, {'XB.otf':set('defDEF'), 'X.otf':set('ABC')}) + s = get_stats('

abcdef

') + # Technically def should not be needed in X but that is hard to achieve + self.assertEqual(s.font_stats, {'XB.otf':set('def'), 'X.otf':set('abcdef')}) def test_remove_property_value(self): style = parseStyle('background-image: url(b.png); background: black url(a.png) fixed')