2011-05-10 12:50:04 +04:00
|
|
|
# coding: utf8
|
2012-03-22 02:19:27 +04:00
|
|
|
"""
|
|
|
|
weasyprint.css.computed_values
|
|
|
|
------------------------------
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-03-22 02:19:27 +04:00
|
|
|
Convert *specified* property values (the result of the cascade and
|
|
|
|
inhertance) into *computed* values (that are inherited).
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-03-22 02:19:27 +04:00
|
|
|
:copyright: Copyright 2011-2012 Simon Sapin and contributors, see AUTHORS.
|
|
|
|
:license: BSD, see LICENSE for details.
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2011-08-23 19:35:10 +04:00
|
|
|
"""
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-02-17 21:49:58 +04:00
|
|
|
from __future__ import division, unicode_literals
|
2012-02-08 18:44:03 +04:00
|
|
|
|
|
|
|
import math
|
2011-05-10 17:00:54 +04:00
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
from .properties import INITIAL_VALUES, Dimension
|
2012-05-30 22:06:44 +04:00
|
|
|
from ..urls import get_link_attribute
|
2012-06-21 17:10:17 +04:00
|
|
|
from .. import text
|
2012-04-03 16:59:06 +04:00
|
|
|
|
|
|
|
|
|
|
|
ZERO_PIXELS = Dimension(0, 'px')
|
2011-05-10 12:50:04 +04:00
|
|
|
|
|
|
|
|
2011-08-23 19:35:10 +04:00
|
|
|
# How many CSS pixels is one <unit>?
|
2011-05-10 17:00:54 +04:00
|
|
|
# http://www.w3.org/TR/CSS21/syndata.html#length-units
|
|
|
|
LENGTHS_TO_PIXELS = {
|
|
|
|
'px': 1,
|
|
|
|
'pt': 1. / 0.75,
|
2011-08-23 19:35:10 +04:00
|
|
|
'pc': 16., # LENGTHS_TO_PIXELS['pt'] * 12
|
|
|
|
'in': 96., # LENGTHS_TO_PIXELS['pt'] * 72
|
|
|
|
'cm': 96. / 2.54, # LENGTHS_TO_PIXELS['in'] / 2.54
|
|
|
|
'mm': 96. / 25.4, # LENGTHS_TO_PIXELS['in'] / 25.4
|
2011-05-10 17:00:54 +04:00
|
|
|
}
|
|
|
|
|
2012-02-08 18:44:03 +04:00
|
|
|
# http://dev.w3.org/csswg/css3-values/#angles
|
|
|
|
# How many radians is one <unit>?
|
|
|
|
ANGLE_TO_RADIANS = {
|
|
|
|
'rad': 1,
|
|
|
|
'turn': 2 * math.pi,
|
|
|
|
'deg': math.pi / 180,
|
|
|
|
'grad': math.pi / 200,
|
|
|
|
}
|
|
|
|
|
2011-05-10 17:00:54 +04:00
|
|
|
# Value in pixels of font-size for <absolute-size> keywords: 12pt (16px) for
|
|
|
|
# medium, and scaling factors given in CSS3 for others:
|
|
|
|
# http://www.w3.org/TR/css3-fonts/#font-size-prop
|
2012-02-20 18:36:55 +04:00
|
|
|
# TODO: this will need to be ordered to implement 'smaller' and 'larger'
|
|
|
|
FONT_SIZE_KEYWORDS = dict(
|
2011-08-18 17:44:45 +04:00
|
|
|
# medium is 16px, others are a ratio of medium
|
2011-12-16 18:18:47 +04:00
|
|
|
(name, INITIAL_VALUES['font_size'] * a / b)
|
2011-08-11 19:26:08 +04:00
|
|
|
for name, a, b in [
|
|
|
|
('xx-small', 3, 5),
|
|
|
|
('x-small', 3, 4),
|
|
|
|
('small', 8, 9),
|
|
|
|
('medium', 1, 1),
|
|
|
|
('large', 6, 5),
|
|
|
|
('x-large', 3, 2),
|
|
|
|
('xx-large', 2, 1),
|
|
|
|
]
|
|
|
|
)
|
2011-05-10 17:00:54 +04:00
|
|
|
|
|
|
|
# These are unspecified, other than 'thin' <='medium' <= 'thick'.
|
|
|
|
# Values are in pixels.
|
|
|
|
BORDER_WIDTH_KEYWORDS = {
|
2011-10-08 16:41:12 +04:00
|
|
|
'thin': 1,
|
|
|
|
'medium': 3,
|
|
|
|
'thick': 5,
|
2011-05-10 17:00:54 +04:00
|
|
|
}
|
2011-12-16 20:53:11 +04:00
|
|
|
assert INITIAL_VALUES['border_top_width'] == BORDER_WIDTH_KEYWORDS['medium']
|
2011-05-10 17:00:54 +04:00
|
|
|
|
|
|
|
# http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
|
|
|
|
FONT_WEIGHT_RELATIVE = dict(
|
|
|
|
bolder={
|
2011-10-08 16:41:12 +04:00
|
|
|
100: 400,
|
|
|
|
200: 400,
|
|
|
|
300: 400,
|
|
|
|
400: 700,
|
|
|
|
500: 700,
|
|
|
|
600: 900,
|
|
|
|
700: 900,
|
|
|
|
800: 900,
|
|
|
|
900: 900,
|
2011-05-10 17:00:54 +04:00
|
|
|
},
|
|
|
|
lighter={
|
2011-10-08 16:41:12 +04:00
|
|
|
100: 100,
|
|
|
|
200: 100,
|
|
|
|
300: 100,
|
|
|
|
400: 100,
|
|
|
|
500: 100,
|
|
|
|
600: 400,
|
|
|
|
700: 400,
|
|
|
|
800: 700,
|
|
|
|
900: 700,
|
2011-05-10 17:00:54 +04:00
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2011-07-11 17:03:06 +04:00
|
|
|
# http://www.w3.org/TR/css3-page/#size
|
|
|
|
# name=(width in pixels, height in pixels)
|
|
|
|
PAGE_SIZES = dict(
|
2012-04-03 21:02:14 +04:00
|
|
|
a5=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(148, 'mm'),
|
|
|
|
Dimension(210, 'mm'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
2012-04-03 21:02:14 +04:00
|
|
|
a4=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(210, 'mm'),
|
|
|
|
Dimension(297, 'mm'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
2012-04-03 21:02:14 +04:00
|
|
|
a3=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(297, 'mm'),
|
|
|
|
Dimension(420, 'mm'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
2012-04-03 21:02:14 +04:00
|
|
|
b5=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(176, 'mm'),
|
|
|
|
Dimension(250, 'mm'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
2012-04-03 21:02:14 +04:00
|
|
|
b4=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(250, 'mm'),
|
|
|
|
Dimension(353, 'mm'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
|
|
|
letter=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(8.5, 'in'),
|
|
|
|
Dimension(11, 'in'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
|
|
|
legal=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(8.5, 'in'),
|
|
|
|
Dimension(14, 'in'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
|
|
|
ledger=(
|
2012-04-03 16:59:06 +04:00
|
|
|
Dimension(11, 'in'),
|
|
|
|
Dimension(17, 'in'),
|
2011-07-11 17:03:06 +04:00
|
|
|
),
|
|
|
|
)
|
2012-04-03 16:59:06 +04:00
|
|
|
# In "portrait" orientation.
|
2011-12-16 18:06:59 +04:00
|
|
|
for w, h in PAGE_SIZES.values():
|
2012-04-03 16:59:06 +04:00
|
|
|
assert w.value < h.value
|
2011-12-16 18:06:59 +04:00
|
|
|
|
2012-04-03 21:02:14 +04:00
|
|
|
INITIAL_PAGE_SIZE = PAGE_SIZES['a4']
|
2012-04-03 16:59:06 +04:00
|
|
|
INITIAL_VALUES['size'] = tuple(
|
|
|
|
d.value * LENGTHS_TO_PIXELS[d.unit] for d in INITIAL_PAGE_SIZE)
|
2011-07-11 17:03:06 +04:00
|
|
|
|
|
|
|
|
2011-12-26 15:47:26 +04:00
|
|
|
def _computing_order():
|
|
|
|
"""Some computed values are required by others, so order matters."""
|
2013-01-23 19:52:47 +04:00
|
|
|
first = ['font_size', 'line_height']
|
2011-12-26 15:47:26 +04:00
|
|
|
order = sorted(INITIAL_VALUES)
|
|
|
|
for name in first:
|
|
|
|
order.remove(name)
|
|
|
|
return tuple(first + order)
|
|
|
|
COMPUTING_ORDER = _computing_order()
|
2011-12-16 21:28:34 +04:00
|
|
|
|
|
|
|
# Maps property names to functions returning the computed values
|
|
|
|
COMPUTER_FUNCTIONS = {}
|
|
|
|
|
|
|
|
|
|
|
|
def register_computer(name):
|
|
|
|
"""Decorator registering a property ``name`` for a function."""
|
|
|
|
name = name.replace('-', '_')
|
|
|
|
def decorator(function):
|
|
|
|
"""Register the property ``name`` for ``function``."""
|
|
|
|
COMPUTER_FUNCTIONS[name] = function
|
|
|
|
return function
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
|
|
def compute(element, pseudo_type, specified, computed, parent_style):
|
|
|
|
"""
|
|
|
|
Return a StyleDict of computed values.
|
2011-06-29 16:04:42 +04:00
|
|
|
|
2011-08-18 17:44:45 +04:00
|
|
|
:param element: The HTML element these style apply to
|
|
|
|
:param pseudo_type: The type of pseudo-element, eg 'before', None
|
2011-08-23 19:35:10 +04:00
|
|
|
:param specified: a :class:`StyleDict` of specified values. Should contain
|
|
|
|
values for all properties.
|
|
|
|
:param computed: a :class:`StyleDict` of already known computed values.
|
|
|
|
Only contains some properties (or none).
|
|
|
|
:param parent_values: a :class:`StyleDict` of computed values of the parent
|
|
|
|
element (should contain values for all properties),
|
|
|
|
or ``None`` if ``element`` is the root element.
|
2011-05-10 12:50:04 +04:00
|
|
|
"""
|
2011-12-16 21:28:34 +04:00
|
|
|
if parent_style is None:
|
|
|
|
parent_style = INITIAL_VALUES
|
2011-08-18 17:44:45 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
computer = lambda: 0 # Dummy object that holds attributes
|
|
|
|
computer.element = element
|
|
|
|
computer.pseudo_type = pseudo_type
|
|
|
|
computer.specified = specified
|
|
|
|
computer.computed = computed
|
|
|
|
computer.parent_style = parent_style
|
2011-08-23 19:35:10 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
getter = COMPUTER_FUNCTIONS.get
|
2011-08-23 19:35:10 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
for name in COMPUTING_ORDER:
|
|
|
|
if name in computed:
|
2011-08-18 17:44:45 +04:00
|
|
|
# Already computed
|
2011-12-16 21:28:34 +04:00
|
|
|
continue
|
2011-08-18 17:44:45 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
value = specified[name]
|
|
|
|
function = getter(name)
|
|
|
|
if function is not None:
|
|
|
|
value = function(computer, name, value)
|
2011-08-18 17:44:45 +04:00
|
|
|
# else: same as specified
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
computed[name] = value
|
2011-08-18 17:44:45 +04:00
|
|
|
|
2012-05-25 19:33:43 +04:00
|
|
|
computed['_weasy_specified_display'] = specified.display
|
2011-12-16 21:28:34 +04:00
|
|
|
return computed
|
2011-08-18 17:44:45 +04:00
|
|
|
|
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
# Let's be consistent, always use ``name`` as an argument even when
|
|
|
|
# it is useless.
|
2011-08-23 19:35:10 +04:00
|
|
|
# pylint: disable=W0613
|
|
|
|
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('background-position')
|
2013-03-19 21:29:58 +04:00
|
|
|
def length_or_percentage_tuple_list(computer, name, values):
|
|
|
|
"""Compute the lists of lengths that can be percentages."""
|
|
|
|
return [length_or_percentage_tuple(computer, name, value)
|
|
|
|
for value in values]
|
|
|
|
|
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
@register_computer('transform-origin')
|
|
|
|
def length_or_percentage_tuple(computer, name, values):
|
|
|
|
"""Compute the lists of lengths that can be percentages."""
|
|
|
|
return tuple(length(computer, name, value) for value in values)
|
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('border-spacing')
|
|
|
|
@register_computer('size')
|
2012-02-07 21:06:59 +04:00
|
|
|
@register_computer('clip')
|
2012-04-03 16:59:06 +04:00
|
|
|
def length_tuple(computer, name, values):
|
2011-10-08 16:41:12 +04:00
|
|
|
"""Compute the properties with a list of lengths."""
|
2012-04-03 16:59:06 +04:00
|
|
|
return tuple(length(computer, name, value, pixels_only=True)
|
|
|
|
for value in values)
|
2011-10-08 16:41:12 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('top')
|
|
|
|
@register_computer('right')
|
|
|
|
@register_computer('left')
|
|
|
|
@register_computer('bottom')
|
|
|
|
@register_computer('margin-top')
|
|
|
|
@register_computer('margin-right')
|
|
|
|
@register_computer('margin-bottom')
|
|
|
|
@register_computer('margin-left')
|
|
|
|
@register_computer('height')
|
|
|
|
@register_computer('width')
|
2012-04-06 18:19:16 +04:00
|
|
|
@register_computer('min-width')
|
|
|
|
@register_computer('min-height')
|
|
|
|
@register_computer('max-width')
|
|
|
|
@register_computer('max-height')
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('padding-top')
|
|
|
|
@register_computer('padding-right')
|
|
|
|
@register_computer('padding-bottom')
|
|
|
|
@register_computer('padding-left')
|
|
|
|
@register_computer('text-indent')
|
2012-04-03 16:59:06 +04:00
|
|
|
def length(computer, name, value, font_size=None, pixels_only=False):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute a length ``value``."""
|
2012-04-05 13:21:26 +04:00
|
|
|
if value == 'auto':
|
|
|
|
return value
|
2012-04-03 16:59:06 +04:00
|
|
|
if value.value == 0:
|
|
|
|
return 0 if pixels_only else ZERO_PIXELS
|
2011-10-08 16:41:12 +04:00
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
unit = value.unit
|
|
|
|
if unit == 'px':
|
|
|
|
return value.value if pixels_only else value
|
|
|
|
elif unit in LENGTHS_TO_PIXELS:
|
2011-08-18 17:44:45 +04:00
|
|
|
# Convert absolute lengths to pixels
|
2012-04-03 16:59:06 +04:00
|
|
|
factor = LENGTHS_TO_PIXELS[unit]
|
|
|
|
elif unit in ('em', 'ex'):
|
2012-02-08 18:44:03 +04:00
|
|
|
if font_size is None:
|
|
|
|
factor = computer.computed.font_size
|
|
|
|
else:
|
|
|
|
factor = font_size
|
2012-04-03 16:59:06 +04:00
|
|
|
if unit == 'ex':
|
|
|
|
# TODO: find a better way to measure ex, see
|
|
|
|
# http://www.w3.org/TR/CSS21/syndata.html#length-units
|
|
|
|
factor *= 0.5
|
|
|
|
else:
|
|
|
|
# A percentage or 'auto': no conversion needed.
|
|
|
|
return value
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
result = value.value * factor
|
|
|
|
return result if pixels_only else Dimension(result, 'px')
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
|
|
|
|
@register_computer('letter-spacing')
|
|
|
|
def pixel_length(computer, name, value):
|
2012-05-14 13:57:55 +04:00
|
|
|
if value == 'normal':
|
|
|
|
return value
|
|
|
|
else:
|
|
|
|
return length(computer, name, value, pixels_only=True)
|
2011-05-10 12:50:04 +04:00
|
|
|
|
|
|
|
|
2012-01-31 14:45:24 +04:00
|
|
|
@register_computer('background-size')
|
2013-03-19 21:29:58 +04:00
|
|
|
def background_size(computer, name, values):
|
2012-01-31 14:45:24 +04:00
|
|
|
"""Compute the ``background-size`` properties."""
|
2013-03-19 21:29:58 +04:00
|
|
|
return [value if value in ('contain', 'cover') else
|
|
|
|
length_or_percentage_tuple(computer, name, value)
|
|
|
|
for value in values]
|
2012-01-31 14:45:24 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('border-top-width')
|
|
|
|
@register_computer('border-right-width')
|
|
|
|
@register_computer('border-left-width')
|
|
|
|
@register_computer('border-bottom-width')
|
2012-08-03 18:21:47 +04:00
|
|
|
@register_computer('outline-width')
|
2011-08-18 17:44:45 +04:00
|
|
|
def border_width(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``border-*-width`` properties."""
|
2011-12-16 21:28:34 +04:00
|
|
|
style = computer.computed[name.replace('width', 'style')]
|
2011-10-08 16:41:12 +04:00
|
|
|
if style in ('none', 'hidden'):
|
|
|
|
return 0
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2011-10-08 16:41:12 +04:00
|
|
|
if value in BORDER_WIDTH_KEYWORDS:
|
|
|
|
return BORDER_WIDTH_KEYWORDS[value]
|
2011-05-10 12:50:04 +04:00
|
|
|
|
2012-04-05 18:31:32 +04:00
|
|
|
if isinstance(value, int):
|
|
|
|
# The initial value can get here, but length() would fail as
|
|
|
|
# it does not have a 'unit' attribute.
|
|
|
|
return value
|
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
return length(computer, name, value, pixels_only=True)
|
2011-05-10 12:50:04 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('content')
|
2011-08-23 19:35:10 +04:00
|
|
|
def content(computer, name, values):
|
|
|
|
"""Compute the ``content`` property."""
|
2011-12-28 18:34:30 +04:00
|
|
|
if values in ('normal', 'none'):
|
|
|
|
return values
|
2011-08-23 19:35:10 +04:00
|
|
|
else:
|
2011-12-28 18:34:30 +04:00
|
|
|
return [('STRING', computer.element.get(value, ''))
|
|
|
|
if type_ == 'attr' else (type_, value)
|
|
|
|
for type_, value in values]
|
2011-08-23 19:35:10 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('display')
|
2011-10-08 16:41:12 +04:00
|
|
|
def display(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``display`` property.
|
|
|
|
|
|
|
|
See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
|
|
|
|
2011-08-18 17:44:45 +04:00
|
|
|
"""
|
2011-10-08 18:34:10 +04:00
|
|
|
float_ = computer.specified.float
|
|
|
|
position = computer.specified.position
|
2011-08-18 17:44:45 +04:00
|
|
|
if position in ('absolute', 'fixed') or float_ != 'none' or \
|
2011-12-16 18:18:47 +04:00
|
|
|
getattr(computer.element, 'getparent', lambda: None)() is None:
|
2011-10-08 16:41:12 +04:00
|
|
|
if value == 'inline-table':
|
|
|
|
return'table'
|
|
|
|
elif value in ('inline', 'table-row-group', 'table-column',
|
|
|
|
'table-column-group', 'table-header-group',
|
|
|
|
'table-footer-group', 'table-row', 'table-cell',
|
|
|
|
'table-caption', 'inline-block'):
|
|
|
|
return 'block'
|
|
|
|
return value
|
2011-08-18 17:44:45 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('float')
|
2011-10-08 16:41:12 +04:00
|
|
|
def compute_float(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``float`` property.
|
|
|
|
|
|
|
|
See http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo
|
|
|
|
|
2011-08-18 17:44:45 +04:00
|
|
|
"""
|
2011-12-16 18:18:47 +04:00
|
|
|
if computer.specified.position in ('absolute', 'fixed'):
|
2011-10-08 16:41:12 +04:00
|
|
|
return 'none'
|
2011-08-18 17:44:45 +04:00
|
|
|
else:
|
2011-10-08 16:41:12 +04:00
|
|
|
return value
|
2011-06-29 16:04:42 +04:00
|
|
|
|
2011-05-12 18:48:37 +04:00
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('font-size')
|
2011-08-18 17:44:45 +04:00
|
|
|
def font_size(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``font-size`` property."""
|
2011-10-08 16:41:12 +04:00
|
|
|
if value in FONT_SIZE_KEYWORDS:
|
|
|
|
return FONT_SIZE_KEYWORDS[value]
|
2012-04-03 16:59:06 +04:00
|
|
|
# TODO: support 'larger' and 'smaller'
|
2011-08-11 19:26:08 +04:00
|
|
|
|
2011-12-16 18:18:47 +04:00
|
|
|
parent_font_size = computer.parent_style['font_size']
|
2012-04-03 16:59:06 +04:00
|
|
|
if value.unit == '%':
|
|
|
|
return value.value * parent_font_size / 100.
|
|
|
|
else:
|
|
|
|
return length(computer, name, value, pixels_only=True,
|
|
|
|
font_size=parent_font_size)
|
2011-08-18 17:44:45 +04:00
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('font-weight')
|
2011-08-18 17:44:45 +04:00
|
|
|
def font_weight(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``font-weight`` property."""
|
2011-10-08 16:41:12 +04:00
|
|
|
if value == 'normal':
|
|
|
|
return 400
|
|
|
|
elif value == 'bold':
|
|
|
|
return 700
|
|
|
|
elif value in ('bolder', 'lighter'):
|
2011-12-16 18:18:47 +04:00
|
|
|
parent_value = computer.parent_style['font_weight']
|
2011-08-18 17:44:45 +04:00
|
|
|
# Use a string here as StyleDict.__setattr__ turns integers into pixel
|
|
|
|
# lengths. This is a number without unit.
|
2011-10-08 16:41:12 +04:00
|
|
|
return FONT_WEIGHT_RELATIVE[value][parent_value]
|
2011-08-18 17:44:45 +04:00
|
|
|
else:
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2011-12-16 21:28:34 +04:00
|
|
|
@register_computer('line-height')
|
2011-08-18 17:44:45 +04:00
|
|
|
def line_height(computer, name, value):
|
2011-08-23 19:35:10 +04:00
|
|
|
"""Compute the ``line-height`` property."""
|
2012-04-03 16:59:06 +04:00
|
|
|
if value == 'normal':
|
2011-12-16 20:31:37 +04:00
|
|
|
return value
|
2012-04-03 16:59:06 +04:00
|
|
|
elif not value.unit:
|
2011-12-16 20:31:37 +04:00
|
|
|
return ('NUMBER', value.value)
|
2012-04-03 16:59:06 +04:00
|
|
|
elif value.unit == '%':
|
2011-08-18 17:44:45 +04:00
|
|
|
factor = value.value / 100.
|
2011-12-16 21:28:34 +04:00
|
|
|
font_size_value = computer.computed.font_size
|
2011-12-16 20:31:37 +04:00
|
|
|
pixels = factor * font_size_value
|
|
|
|
else:
|
2012-04-03 16:59:06 +04:00
|
|
|
pixels = length(computer, name, value, pixels_only=True)
|
2011-12-16 20:31:37 +04:00
|
|
|
return ('PIXELS', pixels)
|
2011-08-18 17:44:45 +04:00
|
|
|
|
|
|
|
|
2012-05-15 21:29:54 +04:00
|
|
|
@register_computer('anchor')
|
|
|
|
def anchor(computer, name, values):
|
|
|
|
"""Compute the ``anchor`` property."""
|
2012-05-29 21:10:46 +04:00
|
|
|
if values != 'none':
|
2012-05-15 21:29:54 +04:00
|
|
|
_, key = values
|
2012-05-29 21:10:46 +04:00
|
|
|
return computer.element.get(key) or None
|
2012-05-15 21:29:54 +04:00
|
|
|
|
|
|
|
|
2012-05-14 20:31:51 +04:00
|
|
|
@register_computer('link')
|
|
|
|
def link(computer, name, values):
|
|
|
|
"""Compute the ``link`` property."""
|
|
|
|
if values == 'none':
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
type_, value = values
|
|
|
|
if type_ == 'attr':
|
2012-05-30 22:06:44 +04:00
|
|
|
return get_link_attribute(computer.element, value)
|
2012-05-14 20:31:51 +04:00
|
|
|
else:
|
2012-05-30 22:06:44 +04:00
|
|
|
return values
|
2012-05-14 21:40:38 +04:00
|
|
|
|
|
|
|
|
2012-12-09 03:41:12 +04:00
|
|
|
@register_computer('lang')
|
|
|
|
def lang(computer, name, values):
|
|
|
|
"""Compute the ``lang`` property."""
|
|
|
|
if values == 'none':
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
type_, key = values
|
|
|
|
if type_ == 'attr':
|
|
|
|
return computer.element.get(key) or None
|
|
|
|
elif type_ == 'string':
|
|
|
|
return key
|
|
|
|
|
|
|
|
|
2012-04-03 16:59:06 +04:00
|
|
|
@register_computer('transform')
|
|
|
|
def transform(computer, name, value):
|
|
|
|
"""Compute the ``transform`` property."""
|
|
|
|
result = []
|
|
|
|
for function, args in value:
|
|
|
|
if function in ('rotate', 'skewx', 'skewy'):
|
|
|
|
args = args.value * ANGLE_TO_RADIANS[args.unit]
|
|
|
|
elif function == 'translate':
|
|
|
|
args = length_or_percentage_tuple(computer, name, args)
|
|
|
|
result.append((function, args))
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2012-01-26 14:33:49 +04:00
|
|
|
@register_computer('vertical-align')
|
2011-10-19 20:18:24 +04:00
|
|
|
def vertical_align(computer, name, value):
|
2012-01-26 14:33:49 +04:00
|
|
|
"""Compute the ``vertical-align`` property."""
|
2011-11-24 14:00:33 +04:00
|
|
|
# Use +/- half an em for super and sub, same as Pango.
|
|
|
|
# (See the SUPERSUB_RISE constant in pango-markup.c)
|
2012-04-03 16:59:06 +04:00
|
|
|
if value in ('baseline', 'middle', 'text-top', 'text-bottom',
|
|
|
|
'top', 'bottom'):
|
|
|
|
return value
|
|
|
|
elif value == 'super':
|
2011-12-16 21:28:34 +04:00
|
|
|
return computer.computed.font_size * 0.5
|
2011-12-16 20:31:37 +04:00
|
|
|
elif value == 'sub':
|
2011-12-16 21:28:34 +04:00
|
|
|
return computer.computed.font_size * -0.5
|
2012-04-03 16:59:06 +04:00
|
|
|
elif value.unit == '%':
|
2012-06-08 18:10:35 +04:00
|
|
|
height, _ = strut_layout(computer.computed)
|
2011-12-26 15:47:26 +04:00
|
|
|
return height * value.value / 100.
|
2011-12-16 20:31:37 +04:00
|
|
|
else:
|
2012-04-03 16:59:06 +04:00
|
|
|
return length(computer, name, value, pixels_only=True)
|
2011-12-16 20:31:37 +04:00
|
|
|
|
|
|
|
|
2012-01-26 14:33:49 +04:00
|
|
|
@register_computer('word-spacing')
|
|
|
|
def word_spacing(computer, name, value):
|
|
|
|
"""Compute the ``word-spacing`` property."""
|
|
|
|
if value == 'normal':
|
|
|
|
return 0
|
|
|
|
else:
|
2012-04-03 16:59:06 +04:00
|
|
|
return length(computer, name, value, pixels_only=True)
|
2012-01-26 14:33:49 +04:00
|
|
|
|
|
|
|
|
2012-06-08 18:10:35 +04:00
|
|
|
def strut_layout(style):
|
|
|
|
"""Return a tuple of the used value of ``line-height`` and the baseline.
|
|
|
|
|
|
|
|
The baseline is given from the top edge of line height.
|
|
|
|
|
|
|
|
"""
|
|
|
|
# TODO: cache these results for a given set of styles?
|
|
|
|
line_height = style.line_height
|
|
|
|
if style.font_size == 0:
|
|
|
|
pango_height = baseline = 0
|
2011-12-16 20:31:37 +04:00
|
|
|
else:
|
2012-06-21 17:10:17 +04:00
|
|
|
# TODO: get the real value for `hinting`? (if we really care…)
|
|
|
|
_, _, _, _, pango_height, baseline = text.split_first_line(
|
2012-11-24 19:14:49 +04:00
|
|
|
'', style, hinting=True, max_width=None, line_width=None)
|
2012-06-08 18:10:35 +04:00
|
|
|
if line_height == 'normal':
|
|
|
|
return pango_height, baseline
|
|
|
|
type_, value = line_height
|
|
|
|
if type_ == 'NUMBER':
|
|
|
|
value *= style.font_size
|
|
|
|
return value, baseline + (value - pango_height) / 2
|