This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
python-emails/emails/loader/stylesheets.py

126 lines
3.6 KiB
Python

# encoding: utf-8
from __future__ import unicode_literals, print_function
import logging
from cssutils.css import CSSStyleSheet
from cssutils import CSSParser
import cssutils
from emails.compat import to_unicode
class PageStylesheets:
"""
Store all html page styles and generates concatenated stylesheet
"""
def __init__(self):
self.urls = set()
self._uri_properties = []
self.sheets = []
self.dirty = True
self.element = None
def update_tag(self):
if self.element is not None:
self._concatenate_sheets()
cssText = self._cached_stylesheet.cssText
cssText = cssText and to_unicode(cssText, 'utf-8') or ''
self.element.text = cssText
def attach_tag(self, element):
self.element = element
def append(self, url=None, text=None, absolute_url=None, local_loader=None):
if (url is not None) and (url in self.urls):
logging.debug('stylesheet url duplicate: %s', url)
return
self.sheets.append({'url': url, 'text': text, 'absolute_url': absolute_url or url,
'local_loader': local_loader})
self.dirty = True
def _concatenate_sheets(self):
if self.dirty or (self._cached_stylesheet is None):
r = CSSStyleSheet()
uri_properties = []
for d in self.sheets:
local_loader = d.get('local_loader', None)
text = d.get('text', None)
uri = d.get('uri', None)
absolute_url = d.get('absolute_url', None)
if (text is None) and local_loader and uri:
text = local_loader[uri]
if text:
sheet = CSSParser().parseString(text, href=absolute_url)
else:
sheet = cssutils.parseUrl(href=absolute_url)
for rule in sheet:
r.add(rule)
for p in _get_rule_uri_properties(rule):
uri_properties.append(p)
self._uri_properties = uri_properties
self._cached_stylesheet = r
self.dirty = False
@property
def stylesheet(self):
self._concatenate_sheets()
return self._cached_stylesheet
@property
def uri_properties(self):
self._concatenate_sheets()
return self._uri_properties
class StyledTagWrapper:
def __init__(self, el):
self.el = el
self.style = CSSParser().parseStyle(el.get('style'))
def update(self):
cssText = self.style.cssText
if isinstance(cssText, str):
cssText = to_unicode(cssText, 'utf-8')
self.el.set('style', cssText)
def uri_properties(self):
for p in self.style.getProperties(all=True):
for v in p.propertyValue:
if v.type == 'URI':
yield v
# Stuff for extracting 'uri-properties' from CSS
# Inspired by cssutils examples
def _style_declarations(base):
"""recursive generator to find all CSSStyleDeclarations"""
if hasattr(base, 'cssRules'):
for rule in base.cssRules:
for s in _style_declarations(rule):
yield s
elif hasattr(base, 'style'):
yield base.style
def _get_rule_uri_properties(rule):
for style in _style_declarations(rule):
for p in style.getProperties(all=True):
for v in p.propertyValue:
if v.type == 'URI':
yield v
def get_stylesheets_uri_properties(sheet):
for rule in sheet:
for p in _get_rule_uri_properties(rule):
yield p