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:
parent
e36006ea21
commit
0faaceb44f
@ -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):
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user