# coding: utf-8 """ weasyprint.css -------------- This module takes care of steps 3 and 4 of “CSS 2.1 processing model”: Retrieve stylesheets associated with a document and annotate every element with a value for every CSS property. http://www.w3.org/TR/CSS21/intro.html#processing-model This module does this in more than two steps. The :func:`get_all_computed_styles` function does everything, but it is itsef based on other functions in this module. :copyright: Copyright 2011-2014 Simon Sapin and contributors, see AUTHORS. :license: BSD, see LICENSE for details. """ from __future__ import division, unicode_literals import re from collections import namedtuple import cssselect2 import tinycss2 from . import properties from . import computed_values from .descriptors import preprocess_descriptors from .validation import (preprocess_declarations, remove_whitespace, split_on_comma) from ..compat import iteritems from ..logger import LOGGER from ..urls import get_url_attribute, url_join, URLFetchingError from .. import CSS # Reject anything not in here: PSEUDO_ELEMENTS = (None, 'before', 'after', 'first-line', 'first-letter') # A test function that returns True if the given property name has an # initial value that is not always the same when computed. RE_INITIAL_NOT_COMPUTED = re.compile( '^(display|column_gap|' '(border_[a-z]+|outline|column_rule)_(width|color))$').match class StyleDict(dict): """A dict allowing attribute access to values. Allow eg. ``style.font_size`` instead of ``style['font-size']``. """ # TODO: this dict should be frozen, but modification is currently # authorized for some corner cases when building the structure: # - wrapping tables, # - removing paddings and margins from tables, # - modifying borders for table cells with collapsing borders, and # - setting viewports and pages overflow. # TODO: We should remove that. Some attributes (eg. "clear") exist as # dict methods and can only be accessed with getitem. __getattr__ = dict.__getitem__ def get_color(self, key): value = self[key] return value if value != 'currentColor' else self['color'] def inherit_from(self): """Return a new StyleDict with inherited properties from this one. Non-inherited properties get their initial values. This is the method used for an anonymous box. """ if '_inherited_style' not in self.__dict__: self._inherited_style = computed_from_cascaded( cascaded={}, parent_style=self, # Only by non-inherited properties, eg `content: attr(href)` element=None) self._inherited_style.anonymous = True return self._inherited_style # Default values, may be overriden on instances anonymous = False PageType = namedtuple('PageType', ['side', 'blank', 'first', 'name']) def get_child_text(element): """Return the text directly in the element, not descendants.""" content = [element.text] if element.text else [] for child in element: if child.tail: content.append(child.tail) return ''.join(content) def find_stylesheets(wrapper_element, device_media_type, url_fetcher, base_url, font_config, page_rules): """Yield the stylesheets in ``element_tree``. The output order is the same as the source order. """ from ..html import element_has_link_type # Work around circular imports. for wrapper in wrapper_element.query_all('style', 'link'): element = wrapper.etree_element mime_type = element.get('type', 'text/css').split(';', 1)[0].strip() # Only keep 'type/subtype' from 'type/subtype ; param1; param2'. if mime_type != 'text/css': continue media_attr = element.get('media', '').strip() or 'all' media = [media_type.strip() for media_type in media_attr.split(',')] if not evaluate_media_query(media, device_media_type): continue if element.tag == 'style': # Content is text that is directly in the