Skip to content

Commit

Permalink
Merge pull request #922 from Kozea/perf
Browse files Browse the repository at this point in the history
Improve performance
  • Loading branch information
liZe authored Sep 3, 2019
2 parents fb62571 + 8ac7afe commit 074d147
Show file tree
Hide file tree
Showing 36 changed files with 534 additions and 505 deletions.
1 change: 0 additions & 1 deletion weasyprint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ def __init__(self, guess=None, filename=None, url=None, file_obj=None,
self.base_url = base_url
self.matcher = matcher or cssselect2.Matcher()
self.page_rules = [] if page_rules is None else page_rules
# TODO: fonts are stored here and should be cleaned after rendering
self.fonts = []
preprocess_stylesheet(
media_type, base_url, stylesheet, url_fetcher, self.matcher,
Expand Down
49 changes: 23 additions & 26 deletions weasyprint/css/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
from .validation.descriptors import preprocess_descriptors

# Reject anything not in here:
PSEUDO_ELEMENTS = (None, 'before', 'after', 'first-line', 'first-letter')
PSEUDO_ELEMENTS = (
None, 'before', 'after', 'marker', 'first-line', 'first-letter')


PageType = namedtuple('PageType', ['side', 'blank', 'first', 'index', 'name'])
Expand Down Expand Up @@ -67,11 +68,14 @@ def __init__(self, html, sheets, presentational_hints, target_collector):
for specificity, attributes in find_style_attributes(
html.etree_element, presentational_hints, html.base_url):
element, declarations, base_url = attributes
style = cascaded_styles.setdefault((element, None), {})
for name, values, importance in preprocess_declarations(
base_url, declarations):
precedence = declaration_precedence('author', importance)
weight = (precedence, specificity)
add_declaration(cascaded_styles, name, values, weight, element)
old_weight = style.get(name, (None, None))[1]
if old_weight is None or old_weight <= weight:
style[name] = values, weight

# First, add declarations and set computed styles for "real" elements
# *in tree order*. Tree order is important so that parents have
Expand All @@ -84,12 +88,14 @@ def __init__(self, html, sheets, presentational_hints, target_collector):
for selector in sheet.matcher.match(element):
specificity, order, pseudo_type, declarations = selector
specificity = sheet_specificity or specificity
style = cascaded_styles.setdefault(
(element.etree_element, pseudo_type), {})
for name, values, importance in declarations:
precedence = declaration_precedence(origin, importance)
weight = (precedence, specificity)
add_declaration(
cascaded_styles, name, values, weight,
element.etree_element, pseudo_type)
old_weight = style.get(name, (None, None))[1]
if old_weight is None or old_weight <= weight:
style[name] = values, weight
parent = element.parent.etree_element if element.parent else None
self.set_computed_styles(
element.etree_element, root=html.etree_element, parent=parent,
Expand Down Expand Up @@ -165,13 +171,15 @@ def add_page_declarations(self, page_type):
specificity, pseudo_type, selector_page_type = selector
if self._page_type_match(selector_page_type, page_type):
specificity = sheet_specificity or specificity
style = self._cascaded_styles.setdefault(
(page_type, pseudo_type), {})
for name, values, importance in declarations:
precedence = declaration_precedence(
origin, importance)
weight = (precedence, specificity)
add_declaration(
self._cascaded_styles, name, values, weight,
page_type, pseudo_type)
old_weight = style.get(name, (None, None))[1]
if old_weight is None or old_weight <= weight:
style[name] = values, weight

def get_cascaded_styles(self):
return self._cascaded_styles
Expand Down Expand Up @@ -573,19 +581,6 @@ def declaration_precedence(origin, importance):
return 5


def add_declaration(cascaded_styles, prop_name, prop_values, weight, element,
pseudo_type=None):
"""Set the value for a property on a given element.
The value is only set if there is no value of greater weight defined yet.
"""
style = cascaded_styles.setdefault((element, pseudo_type), {})
_values, previous_weight = style.get(prop_name, (None, None))
if previous_weight is None or previous_weight <= weight:
style[prop_name] = prop_values, weight


def computed_from_cascaded(element, cascaded, parent_style, pseudo_type=None,
root_style=None, base_url=None,
target_collector=None):
Expand All @@ -602,8 +597,10 @@ def computed_from_cascaded(element, cascaded, parent_style, pseudo_type=None,
# border-*-style is none, so border-width computes to zero.
# Other than that, properties that would need computing are
# border-*-color, but they do not apply.
for side in ('top', 'bottom', 'left', 'right'):
computed['border_%s_width' % side] = 0
computed['border_top_width'] = 0
computed['border_bottom_width'] = 0
computed['border_left_width'] = 0
computed['border_right_width'] = 0
computed['outline_width'] = 0
return computed

Expand All @@ -614,10 +611,10 @@ def computed_from_cascaded(element, cascaded, parent_style, pseudo_type=None,
if parent_style:
for name in parent_style:
if name.startswith('__'):
specified[name] = parent_style[name]
computed[name] = specified[name] = parent_style[name]
for name in cascaded:
if name.startswith('__'):
specified[name] = cascaded[name][0]
computed[name] = specified[name] = cascaded[name][0]

for name, initial in INITIAL_VALUES.items():
if name in cascaded:
Expand Down Expand Up @@ -761,7 +758,7 @@ def parse_page_selectors(rule):

types['index'] = (*nth_values, group)
# TODO: specificity is not specified yet
# https://github.com/w3c/csswg-drafts/issues/3791
# https://github.com/w3c/csswg-drafts/issues/3524
types['specificity'][1] += 1
continue

Expand Down
89 changes: 40 additions & 49 deletions weasyprint/css/computed_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,29 +197,20 @@ def compute(element, pseudo_type, specified, computed, parent_style,
"""
from .validation.properties import PROPERTIES

def computer():
"""Dummy object that holds attributes."""
return 0

computer.is_root_element = parent_style is None
if parent_style is None:
parent_style = INITIAL_VALUES

computer.element = element
computer.pseudo_type = pseudo_type
computer.specified = specified
computer.computed = computed
computer.parent_style = parent_style
computer.root_style = root_style
computer.base_url = base_url
computer.target_collector = target_collector
computer = {
'is_root_element': parent_style is None,
'element': element,
'pseudo_type': pseudo_type,
'specified': specified,
'computed': computed,
'parent_style': parent_style or INITIAL_VALUES,
'root_style': root_style,
'base_url': base_url,
'target_collector': target_collector,
}

getter = COMPUTER_FUNCTIONS.get

for name in specified:
if name.startswith('__'):
computed[name] = specified[name]

for name in COMPUTING_ORDER:
if name in computed:
# Already computed
Expand Down Expand Up @@ -354,24 +345,24 @@ def length(computer, name, value, font_size=None, pixels_only=False):
result = value.value * LENGTHS_TO_PIXELS[unit]
elif unit in ('em', 'ex', 'ch', 'rem'):
if font_size is None:
font_size = computer.computed['font_size']
font_size = computer['computed']['font_size']
if unit == 'ex':
# TODO: cache
result = value.value * font_size * ex_ratio(computer.computed)
result = value.value * font_size * ex_ratio(computer['computed'])
elif unit == 'ch':
# TODO: cache
# TODO: use context to use @font-face fonts
layout = text.Layout(
context=None, font_size=font_size,
style=computer.computed)
style=computer['computed'])
layout.set_text('0')
line, _ = layout.get_first_line()
logical_width, _ = text.get_size(line, computer.computed)
logical_width, _ = text.get_size(line, computer['computed'])
result = value.value * logical_width
elif unit == 'em':
result = value.value * font_size
elif unit == 'rem':
result = value.value * computer.root_style['font_size']
result = value.value * computer['root_style']['font_size']
else:
# A percentage or 'auto': no conversion needed.
return value
Expand All @@ -385,7 +376,7 @@ def length(computer, name, value, font_size=None, pixels_only=False):
@register_computer('bleed-bottom')
def bleed(computer, name, value):
if value == 'auto':
if 'crop' in computer.computed['marks']:
if 'crop' in computer['computed']['marks']:
return Dimension(8, 'px') # 6pt
else:
return Dimension(0, 'px')
Expand Down Expand Up @@ -418,7 +409,7 @@ def background_size(computer, name, values):
@register_computer('outline-width')
def border_width(computer, name, value):
"""Compute the ``border-*-width`` properties."""
style = computer.computed[name.replace('width', 'style')]
style = computer['computed'][name.replace('width', 'style')]
if style in ('none', 'hidden'):
return 0

Expand Down Expand Up @@ -461,19 +452,19 @@ def compute_attr_function(computer, values):
func_name, value = values
assert func_name == 'attr()'
attr_name, type_or_unit, fallback = value
# computer.element sometimes is None
# computer.element sometimes is a 'PageType' object without .get()
# computer['element'] sometimes is None
# computer['element'] sometimes is a 'PageType' object without .get()
# so wrapt the .get() into try and return None instead of crashing
try:
attr_value = computer.element.get(attr_name, fallback)
attr_value = computer['element'].get(attr_name, fallback)
if type_or_unit == 'string':
pass # Keep the string
elif type_or_unit == 'url':
if attr_value.startswith('#'):
attr_value = ('internal', unquote(attr_value[1:]))
else:
attr_value = (
'external', safe_urljoin(computer.base_url, attr_value))
'external', safe_urljoin(computer['base_url'], attr_value))
elif type_or_unit == 'color':
attr_value = parse_color(attr_value.strip())
elif type_or_unit == 'integer':
Expand Down Expand Up @@ -519,12 +510,12 @@ def _content_list(computer, values):
(attr,) + value[1][1:]))
else:
computed_value = value
if computer.target_collector and computed_value:
computer.target_collector.collect_computed_target(
if computer['target_collector'] and computed_value:
computer['target_collector'].collect_computed_target(
computed_value[1][0])
if computed_value is None:
LOGGER.warning('Unable to compute %s\'s value for content: %s' % (
computer.element, ', '.join(str(item) for item in value)))
computer['element'], ', '.join(str(item) for item in value)))
else:
computed_values.append(computed_value)

Expand Down Expand Up @@ -552,7 +543,7 @@ def content(computer, name, values):
if len(values) == 1:
value, = values
if value == 'normal':
return 'inhibit' if computer.pseudo_type else 'contents'
return 'inhibit' if computer['pseudo_type'] else 'contents'
elif value == 'none':
return 'inhibit'
return _content_list(computer, values)
Expand All @@ -565,10 +556,10 @@ def display(computer, name, value):
See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
"""
float_ = computer.specified['float']
position = computer.specified['position']
float_ = computer['specified']['float']
position = computer['specified']['position']
if position in ('absolute', 'fixed') or float_ != 'none' or \
computer.is_root_element:
computer['is_root_element']:
if value == 'inline-table':
return'table'
elif value in ('inline', 'table-row-group', 'table-column',
Expand All @@ -586,7 +577,7 @@ def compute_float(computer, name, value):
See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
"""
if computer.specified['position'] in ('absolute', 'fixed'):
if computer['specified']['position'] in ('absolute', 'fixed'):
return 'none'
else:
return value
Expand All @@ -599,7 +590,7 @@ def font_size(computer, name, value):
return FONT_SIZE_KEYWORDS[value]
# TODO: support 'larger' and 'smaller'

parent_font_size = computer.parent_style['font_size']
parent_font_size = computer['parent_style']['font_size']
if value.unit == '%':
return value.value * parent_font_size / 100.
else:
Expand All @@ -615,7 +606,7 @@ def font_weight(computer, name, value):
elif value == 'bold':
return 700
elif value in ('bolder', 'lighter'):
parent_value = computer.parent_style['font_weight']
parent_value = computer['parent_style']['font_weight']
return FONT_WEIGHT_RELATIVE[value][parent_value]
else:
return value
Expand All @@ -630,7 +621,7 @@ def line_height(computer, name, value):
return ('NUMBER', value.value)
elif value.unit == '%':
factor = value.value / 100.
font_size_value = computer.computed['font_size']
font_size_value = computer['computed']['font_size']
pixels = factor * font_size_value
else:
pixels = length(computer, name, value, pixels_only=True)
Expand All @@ -642,8 +633,8 @@ def anchor(computer, name, values):
"""Compute the ``anchor`` property."""
if values != 'none':
_, key = values
anchor_name = computer.element.get(key) or None
computer.target_collector.collect_anchor(anchor_name)
anchor_name = computer['element'].get(key) or None
computer['target_collector'].collect_anchor(anchor_name)
return anchor_name


Expand All @@ -656,7 +647,7 @@ def link(computer, name, values):
type_, value = values
if type_ == 'attr()':
return get_link_attribute(
computer.element, value, computer.base_url)
computer['element'], value, computer['base_url'])
else:
return values

Expand All @@ -669,7 +660,7 @@ def lang(computer, name, values):
else:
type_, key = values
if type_ == 'attr()':
return computer.element.get(key) or None
return computer['element'].get(key) or None
elif type_ == 'string':
return key

Expand Down Expand Up @@ -703,11 +694,11 @@ def vertical_align(computer, name, value):
'top', 'bottom'):
return value
elif value == 'super':
return computer.computed['font_size'] * 0.5
return computer['computed']['font_size'] * 0.5
elif value == 'sub':
return computer.computed['font_size'] * -0.5
return computer['computed']['font_size'] * -0.5
elif value.unit == '%':
height, _ = strut_layout(computer.computed)
height, _ = strut_layout(computer['computed'])
return height * value.value / 100.
else:
return length(computer, name, value, pixels_only=True)
Expand Down
1 change: 1 addition & 0 deletions weasyprint/css/html5_ua.css
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ track { display: none; }
tt { font-family: monospace; }
u { text-decoration: underline; }

::marker { unicode-bidi: isolate; font-variant-numeric: tabular-nums; }
ul { display: block; list-style-type: disc; margin-bottom: 1em; margin-top: 1em; padding-left: 40px; unicode-bidi: isolate; }

*[dir=ltr] ul { padding-left: 40px; padding-right: 0; }
Expand Down
2 changes: 2 additions & 0 deletions weasyprint/css/tests_ua.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ h3 { bookmark-level: 3; bookmark-label: content(text); }
h4 { bookmark-level: 4; bookmark-label: content(text); }
h5 { bookmark-level: 5; bookmark-label: content(text); }
h6 { bookmark-level: 6; bookmark-label: content(text); }

::marker { unicode-bidi: isolate; font-variant-numeric: tabular-nums; }
Loading

0 comments on commit 074d147

Please sign in to comment.