1
1
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:
Simon Sapin 2012-01-31 11:45:24 +01:00
parent 1d2b5ce610
commit 70292e1dae
5 changed files with 172 additions and 7 deletions

View File

@ -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')

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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."""