Skip to content

Commit

Permalink
Merge pull request #1587 from aschmitz/perf-20220302
Browse files Browse the repository at this point in the history
perf: cache calculations for 'ch' and 'ex' units
  • Loading branch information
liZe authored Mar 19, 2022
2 parents 9b4150c + 4833bf6 commit 3538b1c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 17 deletions.
13 changes: 13 additions & 0 deletions weasyprint/css/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
None, 'before', 'after', 'marker', 'first-line', 'first-letter',
'footnote-call', 'footnote-marker')

DEFAULT_CACHE = {
'ratio_ch': {},
'ratio_ex': {},
}


PageType = namedtuple('PageType', ['side', 'blank', 'first', 'index', 'name'])

Expand Down Expand Up @@ -600,6 +605,10 @@ def __init__(self, parent_style):
})
self.parent_style = parent_style
self.specified = self
if parent_style:
self.cache = parent_style.cache
else:
self.cache = DEFAULT_CACHE.copy()

def copy(self):
copy = AnonymousStyle(self.parent_style)
Expand Down Expand Up @@ -629,6 +638,10 @@ def __init__(self, parent_style, cascaded, element, pseudo_type,
self.pseudo_type = pseudo_type
self.root_style = root_style
self.base_url = base_url
if parent_style:
self.cache = parent_style.cache
else:
self.cache = DEFAULT_CACHE.copy()

def copy(self):
copy = ComputedStyle(
Expand Down
57 changes: 42 additions & 15 deletions weasyprint/css/computed_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ..logger import LOGGER
from ..text.ffi import ffi, pango, units_to_double
from ..text.line_break import Layout, first_line_metrics, line_size
from ..text.line_break import Layout, first_line_metrics
from ..urls import get_link_attribute
from .properties import (
INHERITED, INITIAL_NOT_COMPUTED, INITIAL_VALUES, Dimension)
Expand Down Expand Up @@ -178,6 +178,22 @@ def _resolve_var(computed, variable_name, default, parent_style):
return computed_value


def _font_style_cache_key(style):
return str((
style['font_family'],
style['font_style'],
style['font_stretch'],
style['font_weight'],
style['font_variant_ligatures'],
style['font_variant_position'],
style['font_variant_caps'],
style['font_variant_numeric'],
style['font_variant_alternates'],
style['font_variant_east_asian'],
style['font_feature_settings'],
))


def register_computer(name):
"""Decorator registering a property ``name`` for a function."""
name = name.replace('-', '_')
Expand Down Expand Up @@ -321,18 +337,26 @@ def length(style, name, value, font_size=None, pixels_only=False):
if font_size is None:
font_size = style['font_size']
if unit == 'ex':
# TODO: cache
result = value.value * font_size * ex_ratio(style)
cache_key = _font_style_cache_key(style)

if cache_key in style.cache['ratio_ex']:
ratio = style.cache['ratio_ex'][cache_key]
else:
ratio = _character_ratio(style, 'x')
style.cache['ratio_ex'][cache_key] = ratio

result = value.value * font_size * ratio
elif unit == 'ch':
# TODO: cache
# TODO: use context to use @font-face fonts
layout = Layout(
context=None, font_size=font_size,
style=style)
layout.set_text('0')
line, _ = layout.get_first_line()
logical_width, _ = line_size(line, style)
result = value.value * logical_width
cache_key = _font_style_cache_key(style)

if cache_key in style.cache['ratio_ch']:
ratio = style.cache['ratio_ch'][cache_key]
else:
ratio = _character_ratio(style, '0')
style.cache['ratio_ch'][cache_key] = ratio

result = value.value * font_size * ratio
elif unit == 'em':
result = value.value * font_size
elif unit == 'rem':
Expand Down Expand Up @@ -745,7 +769,7 @@ def strut_layout(style, context=None):
return result


def ex_ratio(style):
def _character_ratio(style, character):
"""Return the ratio 1ex/font_size, according to given style."""
# TODO: use context to use @font-face fonts

Expand All @@ -758,14 +782,17 @@ def ex_ratio(style):
font_size = 1000

layout = Layout(context=None, font_size=font_size, style=style)
layout.set_text('x')
layout.set_text(character)
line, _ = layout.get_first_line()

ink_extents = ffi.new('PangoRectangle *')
pango.pango_layout_line_get_extents(line, ink_extents, ffi.NULL)
height_above_baseline = units_to_double(ink_extents.y)
if character == 'x':
measure = units_to_double(ink_extents.y)
else:
measure = units_to_double(ink_extents.width)
ffi.release(ink_extents)

# Zero means some kind of failure, fallback is 0.5.
# We round to try keeping exact values that were altered by Pango.
return round(-height_above_baseline / font_size, 5) or 0.5
return round(measure / font_size, 5) or 0.5
8 changes: 6 additions & 2 deletions weasyprint/layout/inline.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
from math import inf

from ..css import computed_from_cascaded
from ..css.computed_values import ex_ratio, strut_layout
from ..css.computed_values import length as computed_length
from ..css.computed_values import strut_layout
from ..css.properties import Dimension
from ..formatting_structure import boxes
from ..text.line_break import can_break_text, create_layout, split_first_line
from .absolute import AbsolutePlaceholder, absolute_layout
Expand Down Expand Up @@ -1048,7 +1050,9 @@ def inline_box_verticality(box, top_bottom_subtrees, baseline_y):
if vertical_align == 'baseline':
child_baseline_y = baseline_y
elif vertical_align == 'middle':
one_ex = box.style['font_size'] * ex_ratio(box.style)
one_ex = computed_length(
box.style, 'height', Dimension(1, 'em'),
box.style['font_size'], pixels_only=True)
top = baseline_y - (one_ex + child.margin_height()) / 2
child_baseline_y = top + child.baseline
elif vertical_align == 'text-top':
Expand Down

0 comments on commit 3538b1c

Please sign in to comment.