1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-04 16:07:57 +03:00

Have Image objects "render themselves" on a cairo Context.

This commit is contained in:
Simon Sapin 2013-04-03 16:00:31 +02:00
parent e36006ea21
commit 0faaceb44f
2 changed files with 37 additions and 42 deletions

View File

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

View File

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