1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-10-05 00:21:15 +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 .text import show_first_line
from .compat import xrange 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 @contextlib.contextmanager
def stacked(context): def stacked(context):
@ -304,12 +296,9 @@ def draw_background_image(context, layer):
sub_surface = cairo.PDFSurface(None, repeat_width, repeat_height) sub_surface = cairo.PDFSurface(None, repeat_width, repeat_height)
sub_context = cairo.Context(sub_surface) sub_context = cairo.Context(sub_surface)
sub_context.rectangle(0, 0, image_width, image_height) sub_context.rectangle(0, 0, image_width, image_height)
sub_context.scale(image_width / intrinsic_width, sub_context.clip()
image_height / intrinsic_height) layer.image.draw(
sub_pattern = layer.image.get_pattern() sub_context, image_width, image_height, layer.image_rendering)
sub_pattern.set_filter(IMAGE_RENDERING_TO_FILTER[layer.image_rendering])
sub_context.set_source(sub_pattern)
sub_context.fill()
pattern = cairo.SurfacePattern(sub_surface) pattern = cairo.SurfacePattern(sub_surface)
pattern.set_extend(cairo.EXTEND_REPEAT) pattern.set_extend(cairo.EXTEND_REPEAT)
@ -637,27 +626,15 @@ def draw_collapsed_borders(context, table, enable_hinting):
def draw_replacedbox(context, box): def draw_replacedbox(context, box):
"""Draw the given :class:`boxes.ReplacedBox` to a ``cairo.context``.""" """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 return
x, y = box.content_box_x(), box.content_box_y() with stacked(context):
width, height = box.width, box.height context.translate(box.content_box_x(), box.content_box_y())
scale_width = width / box.replacement.intrinsic_width context.rectangle(0, 0, box.width, box.height)
scale_height = height / box.replacement.intrinsic_height context.clip()
# Draw nothing for width:0 or height:0 box.replacement.draw(
if scale_width != 0 and scale_height != 0: context, box.width, box.height, box.style.image_rendering)
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()
def draw_inline_level(context, page, box, enable_hinting): def draw_inline_level(context, page, box, enable_hinting):

View File

@ -31,6 +31,15 @@ except OSError:
from .logger import LOGGER 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): class RasterImage(object):
def __init__(self, image_surface): def __init__(self, image_surface):
self.image_surface = image_surface self.image_surface = image_surface
@ -38,8 +47,14 @@ class RasterImage(object):
self.intrinsic_height = image_surface.get_height() self.intrinsic_height = image_surface.get_height()
self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height
def get_pattern(self): def draw(self, context, concrete_width, concrete_height, image_rendering):
return cairocffi.SurfacePattern(self.image_surface) 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): class ScaledSVGSurface(cairosvg.surface.SVGSurface):
@ -62,14 +77,14 @@ class SVGImage(object):
self._svg_data = svg_data self._svg_data = svg_data
# TODO: find a way of not doing twice the whole rendering. # 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 # TODO: support SVG images with none or only one of intrinsic
# width, height and ratio. # 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( raise ValueError(
'SVG Images without an intrinsic size are not supported.') 'SVG Images without an intrinsic size are not supported.')
self.intrinsic_width = cairosvg_result.width self.intrinsic_width = svg.width
self.intrinsic_height = cairosvg_result.height self.intrinsic_height = svg.height
self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height self.intrinsic_ratio = self.intrinsic_width / self.intrinsic_height
def _render(self): def _render(self):
@ -79,11 +94,14 @@ class SVGImage(object):
cairosvg.parser.Tree(bytestring=self._svg_data, url=self._base_url), cairosvg.parser.Tree(bytestring=self._svg_data, url=self._base_url),
output=None, dpi=96) output=None, dpi=96)
def get_pattern(self): def draw(self, context, concrete_width, concrete_height, image_rendering):
# Do not re-use the Surface object, but regenerate it as needed. # 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 # 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. # 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): def get_image_from_uri(cache, url_fetcher, uri, forced_mime_type=None):