mirror of
https://github.com/Kozea/WeasyPrint.git
synced 2024-10-05 00:21:15 +03:00
Implement background-size and image-rendering.
This commit is contained in:
parent
1d2b5ce610
commit
70292e1dae
@ -261,6 +261,15 @@ def length(computer, name, value):
|
||||
return value.value * factor
|
||||
|
||||
|
||||
@register_computer('background-size')
|
||||
def border_width(computer, name, value):
|
||||
"""Compute the ``background-size`` properties."""
|
||||
if value in ('contain', 'cover'):
|
||||
return value
|
||||
else:
|
||||
return length_list(computer, name, value)
|
||||
|
||||
|
||||
@register_computer('border-top-width')
|
||||
@register_computer('border-right-width')
|
||||
@register_computer('border-left-width')
|
||||
|
@ -33,6 +33,7 @@ INITIAL_VALUES = {
|
||||
'background_repeat': 'repeat',
|
||||
'background_clip': 'border-box', # CSS3
|
||||
'background_origin': 'padding-box', # CSS3
|
||||
'background_size': ('auto', 'auto'), # CSS3
|
||||
'border_collapse': 'separate',
|
||||
# http://www.w3.org/TR/css3-color/#currentcolor
|
||||
'border_top_color': 'currentColor',
|
||||
@ -117,6 +118,10 @@ INITIAL_VALUES = {
|
||||
|
||||
# CSS3 User Interface: http://www.w3.org/TR/css3-ui/#box-sizing
|
||||
'box_sizing': 'content-box',
|
||||
|
||||
# Taken from SVG:
|
||||
# http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
|
||||
'image_rendering': 'auto',
|
||||
}
|
||||
|
||||
# Not applicable to the print media
|
||||
|
@ -223,6 +223,30 @@ def background_repeat(keyword):
|
||||
return keyword in ('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
|
||||
|
||||
|
||||
@validator()
|
||||
def background_size(values):
|
||||
"""Validation for ``background-size``."""
|
||||
if len(values) == 1:
|
||||
value = values[0]
|
||||
keyword = get_keyword(value)
|
||||
if keyword in ('contain', 'cover'):
|
||||
return keyword
|
||||
if keyword == 'auto':
|
||||
return ('auto', 'auto')
|
||||
if is_dimension_or_percentage(value, negative=False):
|
||||
return (value, 'auto')
|
||||
elif len(values) == 2:
|
||||
new_values = []
|
||||
for value in values:
|
||||
if get_keyword(value) == 'auto':
|
||||
new_values.append('auto')
|
||||
elif is_dimension_or_percentage(value, negative=False):
|
||||
new_values.append(value)
|
||||
else:
|
||||
return
|
||||
return tuple(values)
|
||||
|
||||
|
||||
@validator('background_clip')
|
||||
@validator('background_origin')
|
||||
@single_keyword
|
||||
@ -626,7 +650,14 @@ def white_space(keyword):
|
||||
return keyword in ('normal', 'pre', 'nowrap', 'pre-wrap', 'pre-line')
|
||||
|
||||
|
||||
@validator(prefixed=True)
|
||||
@validator(prefixed=True) # Taken from SVG
|
||||
@single_keyword
|
||||
def image_rendering(keyword):
|
||||
"""Validation for ``image-rendering``."""
|
||||
return keyword in ('auto', 'optimizeSpeed', 'optimizeQuality')
|
||||
|
||||
|
||||
@validator(prefixed=True) # Not in CR yet
|
||||
def size(values):
|
||||
"""``size`` property validation.
|
||||
|
||||
|
@ -31,6 +31,14 @@ from .formatting_structure import boxes
|
||||
from .css.values import get_percentage_value
|
||||
|
||||
|
||||
# Map values of the image-rendering property to cairo FILTER values:
|
||||
IMAGE_RENDERING_TO_FILTER = dict(
|
||||
optimizeSpeed=cairo.FILTER_FAST,
|
||||
auto=cairo.FILTER_GOOD,
|
||||
optimizeQuality=cairo.FILTER_BEST,
|
||||
)
|
||||
|
||||
|
||||
class CairoContext(cairo.Context):
|
||||
"""A ``cairo.Context`` with a few more helper methods."""
|
||||
|
||||
@ -171,12 +179,6 @@ def draw_background(document, context, style, painting_area, positioning_area):
|
||||
if image is None:
|
||||
return
|
||||
|
||||
pattern, image_width, image_height = image
|
||||
|
||||
(positioning_x, positioning_y,
|
||||
positioning_width, positioning_height) = positioning_area
|
||||
context.translate(positioning_x, positioning_y)
|
||||
|
||||
def percentage(value, refer_to):
|
||||
percentage_value = get_percentage_value(value)
|
||||
if percentage_value is None:
|
||||
@ -184,6 +186,37 @@ def draw_background(document, context, style, painting_area, positioning_area):
|
||||
else:
|
||||
return refer_to * percentage_value / 100
|
||||
|
||||
(positioning_x, positioning_y,
|
||||
positioning_width, positioning_height) = positioning_area
|
||||
context.translate(positioning_x, positioning_y)
|
||||
|
||||
pattern, intrinsic_width, intrinsic_height = image
|
||||
|
||||
bg_size = style.background_size
|
||||
if bg_size in ('cover', 'contain'):
|
||||
scale_x = scale_y = {'cover': max, 'contain': min}[bg_size](
|
||||
positioning_width / intrinsic_width,
|
||||
positioning_height / intrinsic_height)
|
||||
image_width = intrinsic_width * scale_x
|
||||
image_height = intrinsic_height * scale_y
|
||||
elif bg_size == ('auto', 'auto'):
|
||||
scale_x = scale_y = 1
|
||||
image_width = intrinsic_width
|
||||
image_height = intrinsic_height
|
||||
elif bg_size[0] == 'auto':
|
||||
image_height = percentage(bg_size[1], positioning_height)
|
||||
scale_x = scale_y = image_height / intrinsic_height
|
||||
image_width = intrinsic_width * scale_x
|
||||
elif bg_size[1] == 'auto':
|
||||
image_width = percentage(bg_size[0], positioning_width)
|
||||
scale_x = scale_y = image_width / intrinsic_width
|
||||
image_height = intrinsic_height * scale_y
|
||||
else:
|
||||
image_width = percentage(bg_size[0], positioning_width)
|
||||
image_height = percentage(bg_size[1], positioning_height)
|
||||
scale_x = image_width / intrinsic_width
|
||||
scale_y = image_height / intrinsic_height
|
||||
|
||||
bg_position_x, bg_position_y = style.background_position
|
||||
context.translate(
|
||||
percentage(bg_position_x, positioning_width - image_width),
|
||||
@ -218,6 +251,9 @@ def draw_background(document, context, style, painting_area, positioning_area):
|
||||
pattern.set_extend(cairo.EXTEND_NONE)
|
||||
else:
|
||||
pattern.set_extend(cairo.EXTEND_REPEAT)
|
||||
# TODO: de-duplicate this with draw_replacedbox()
|
||||
pattern.set_filter(IMAGE_RENDERING_TO_FILTER[style.image_rendering])
|
||||
context.scale(scale_x, scale_y)
|
||||
context.set_source(pattern)
|
||||
context.paint()
|
||||
|
||||
@ -427,6 +463,8 @@ def draw_replacedbox(context, box):
|
||||
context.scale(scale_width, scale_height)
|
||||
# The same image/pattern may have been used in a repeating background.
|
||||
pattern.set_extend(cairo.EXTEND_NONE)
|
||||
pattern.set_filter(IMAGE_RENDERING_TO_FILTER[
|
||||
box.style.image_rendering])
|
||||
context.set_source(pattern)
|
||||
context.paint()
|
||||
|
||||
|
@ -59,6 +59,8 @@ def format_pixel(lines, x, y):
|
||||
|
||||
def assert_pixels(name, expected_width, expected_height, expected_lines, html):
|
||||
"""Helper testing the size of the image and the pixels values."""
|
||||
assert len(expected_lines) == expected_height
|
||||
assert len(expected_lines[0]) == expected_width * BYTES_PER_PIXELS
|
||||
_doc, lines = html_to_png(name, expected_width, expected_height, html)
|
||||
assert_pixels_equal(name, expected_width, expected_height, lines,
|
||||
expected_lines)
|
||||
@ -574,6 +576,7 @@ def test_background_image():
|
||||
|
||||
@SUITE.test
|
||||
def test_background_origin():
|
||||
"""Test the background-origin property."""
|
||||
def test_value(value, pixels, css=None):
|
||||
assert_pixels('background_origin_' + value, 12, 12, pixels, '''
|
||||
<style>
|
||||
@ -649,6 +652,7 @@ def test_background_origin():
|
||||
|
||||
@SUITE.test
|
||||
def test_background_clip():
|
||||
"""Test the background-clip property."""
|
||||
def test_value(value, pixels):
|
||||
assert_pixels('background_clip_' + value, 8, 8, pixels, '''
|
||||
<style>
|
||||
@ -693,6 +697,84 @@ def test_background_clip():
|
||||
])
|
||||
|
||||
|
||||
@SUITE.test
|
||||
def test_background_size():
|
||||
"""Test the background-size property."""
|
||||
assert_pixels('background_size', 12, 12, [
|
||||
_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+_+_+r+r+B+B+B+B+B+B+_,
|
||||
_+_+_+r+r+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], '''
|
||||
<style>
|
||||
@page { -weasy-size: 12px }
|
||||
html { background: #fff }
|
||||
body { margin: 1px; height: 10px;
|
||||
/* Use nearest neighbor algorithm for image resizing: */
|
||||
-weasy-image-rendering: optimizeSpeed;
|
||||
background: url(pattern.png) bottom right no-repeat;
|
||||
background-size: 8px }
|
||||
</style>
|
||||
<body>
|
||||
''')
|
||||
|
||||
assert_pixels('background_size_contain', 14, 10, [
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+r+r+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+r+r+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+B+B+B+B+B+B+B+B+_+_+_+_+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], '''
|
||||
<style>
|
||||
@page { -weasy-size: 14px 10px }
|
||||
html { background: #fff }
|
||||
body { margin: 1px; height: 8px;
|
||||
/* Use nearest neighbor algorithm for image resizing: */
|
||||
-weasy-image-rendering: optimizeSpeed;
|
||||
background: url(pattern.png) no-repeat;
|
||||
background-size: contain }
|
||||
</style>
|
||||
<body>
|
||||
''')
|
||||
|
||||
assert_pixels('background_size_cover', 14, 10, [
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
_+r+r+r+B+B+B+B+B+B+B+B+B+_,
|
||||
_+r+r+r+B+B+B+B+B+B+B+B+B+_,
|
||||
_+r+r+r+B+B+B+B+B+B+B+B+B+_,
|
||||
_+B+B+B+B+B+B+B+B+B+B+B+B+_,
|
||||
_+B+B+B+B+B+B+B+B+B+B+B+B+_,
|
||||
_+B+B+B+B+B+B+B+B+B+B+B+B+_,
|
||||
_+B+B+B+B+B+B+B+B+B+B+B+B+_,
|
||||
_+B+B+B+B+B+B+B+B+B+B+B+B+_,
|
||||
_+_+_+_+_+_+_+_+_+_+_+_+_+_,
|
||||
], '''
|
||||
<style>
|
||||
@page { -weasy-size: 14px 10px }
|
||||
html { background: #fff }
|
||||
body { margin: 1px; height: 8px;
|
||||
/* Use nearest neighbor algorithm for image resizing: */
|
||||
-weasy-image-rendering: optimizeSpeed;
|
||||
background: url(pattern.png) no-repeat;
|
||||
background-size: cover }
|
||||
</style>
|
||||
<body>
|
||||
''')
|
||||
|
||||
|
||||
@SUITE.test
|
||||
def test_list_style_image():
|
||||
"""Test images as list markers."""
|
||||
|
Loading…
Reference in New Issue
Block a user