diff --git a/weasyprint/draw.py b/weasyprint/draw.py index c1b3b065..e1c60853 100644 --- a/weasyprint/draw.py +++ b/weasyprint/draw.py @@ -24,14 +24,6 @@ from .stacking import StackingContext from .text import show_first_line from .compat import xrange -# Map values of the image-rendering property to cairo FILTER values: -# Values are normalized to lower case. -IMAGE_RENDERING_TO_FILTER = dict( - optimizespeed=cairo.FILTER_FAST, - auto=cairo.FILTER_GOOD, - optimizequality=cairo.FILTER_BEST, -) - @contextlib.contextmanager def stacked(context): @@ -304,12 +296,9 @@ def draw_background_image(context, layer): sub_surface = cairo.PDFSurface(None, repeat_width, repeat_height) sub_context = cairo.Context(sub_surface) sub_context.rectangle(0, 0, image_width, image_height) - sub_context.scale(image_width / intrinsic_width, - image_height / intrinsic_height) - sub_pattern = layer.image.get_pattern() - sub_pattern.set_filter(IMAGE_RENDERING_TO_FILTER[layer.image_rendering]) - sub_context.set_source(sub_pattern) - sub_context.fill() + sub_context.clip() + layer.image.draw( + sub_context, image_width, image_height, layer.image_rendering) pattern = cairo.SurfacePattern(sub_surface) pattern.set_extend(cairo.EXTEND_REPEAT) @@ -637,27 +626,15 @@ def draw_collapsed_borders(context, table, enable_hinting): def draw_replacedbox(context, box): """Draw the given :class:`boxes.ReplacedBox` to a ``cairo.context``.""" - if box.style.visibility == 'hidden': + if box.style.visibility == 'hidden' or box.width == 0 or box.height == 0: return - x, y = box.content_box_x(), box.content_box_y() - width, height = box.width, box.height - scale_width = width / box.replacement.intrinsic_width - scale_height = height / box.replacement.intrinsic_height - # Draw nothing for width:0 or height:0 - if scale_width != 0 and scale_height != 0: - with stacked(context): - context.translate(x, y) - context.rectangle(0, 0, width, height) - context.clip() - context.scale(scale_width, scale_height) - pattern = box.replacement.get_pattern() - # We might get a shared Pattern that was previously used - # in a repeating background. - pattern.set_filter(IMAGE_RENDERING_TO_FILTER[ - box.style.image_rendering]) - context.set_source(pattern) - context.paint() + with stacked(context): + context.translate(box.content_box_x(), box.content_box_y()) + context.rectangle(0, 0, box.width, box.height) + context.clip() + box.replacement.draw( + context, box.width, box.height, box.style.image_rendering) def draw_inline_level(context, page, box, enable_hinting): diff --git a/weasyprint/images.py b/weasyprint/images.py index a510c317..2ab491a3 100644 --- a/weasyprint/images.py +++ b/weasyprint/images.py @@ -31,6 +31,15 @@ except OSError: from .logger import LOGGER +# Map values of the image-rendering property to cairo FILTER values: +# Values are normalized to lower case. +IMAGE_RENDERING_TO_FILTER = dict( + optimizespeed=cairocffi.FILTER_FAST, + auto=cairocffi.FILTER_GOOD, + optimizequality=cairocffi.FILTER_BEST, +) + + class RasterImage(object): def __init__(self, image_surface): self.image_surface = image_surface @@ -38,8 +47,14 @@ class RasterImage(object): self.intrinsic_height = image_surface.get_height() self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height - def get_pattern(self): - return cairocffi.SurfacePattern(self.image_surface) + def draw(self, context, concrete_width, concrete_height, image_rendering): + if self.intrinsic_width > 0 and self.intrinsic_height > 0: + context.scale(concrete_width / self.intrinsic_width, + concrete_height / self.intrinsic_height) + context.set_source_surface(self.image_surface) + context.get_source().set_filter( + IMAGE_RENDERING_TO_FILTER[image_rendering]) + context.paint() class ScaledSVGSurface(cairosvg.surface.SVGSurface): @@ -62,14 +77,14 @@ class SVGImage(object): self._svg_data = svg_data # TODO: find a way of not doing twice the whole rendering. - cairosvg_result = self._render() + svg = self._render() # TODO: support SVG images with none or only one of intrinsic # width, height and ratio. - if not (cairosvg_result.width > 0 and cairosvg_result.height > 0): + if not (svg.width > 0 and svg.height > 0): raise ValueError( 'SVG Images without an intrinsic size are not supported.') - self.intrinsic_width = cairosvg_result.width - self.intrinsic_height = cairosvg_result.height + self.intrinsic_width = svg.width + self.intrinsic_height = svg.height self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height def _render(self): @@ -79,11 +94,14 @@ class SVGImage(object): cairosvg.parser.Tree(bytestring=self._svg_data, url=self._base_url), output=None, dpi=96) - def get_pattern(self): - # Do not re-use the Surface object, but regenerate it as needed. + def draw(self, context, concrete_width, concrete_height, image_rendering): + # Do not re-use the rendered Surface object, but regenerate it as needed. # If a surface for a SVG image is still alive by the time we call # show_page(), cairo will rasterize the image instead writing vectors. - return cairocffi.SurfacePattern(self._render().cairo) + svg = self._render() + context.scale(concrete_width / svg.width, concrete_height / svg.height) + context.set_source_surface(svg.cairo) + context.paint() def get_image_from_uri(cache, url_fetcher, uri, forced_mime_type=None):