1
1
mirror of https://github.com/Kozea/WeasyPrint.git synced 2024-09-11 20:47:56 +03:00

More fixes around SVG gradients

This commit is contained in:
Guillaume Ayoub 2022-11-18 22:59:56 +01:00
parent 1e6424d54c
commit c4c23fca73
5 changed files with 63 additions and 31 deletions

View File

@ -173,6 +173,42 @@ def test_linear_gradient_transform(assert_pixels):
''')
@assert_no_logs
def test_linear_gradient_transform_userspace(assert_pixels):
assert_pixels('''
__________
BBBBBBBBBB
BBBBBBBBBB
BBBBBBBBBB
BBBBBBBBBB
BBBBBBBBBB
RRRRRRRRRR
GGGGGGGGGG
vvvvvvvvvv
__________
''', '''
<style>
@page { size: 10px 10px}
svg { display: block }
</style>
<svg width="10px" height="10px" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad" x1="0" y1="0" x2="0" y2="8"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.5, 0, 0, 0.5, 0, 5)">
<stop stop-color="blue" offset="25%"></stop>
<stop stop-color="red" offset="25%"></stop>
<stop stop-color="red" offset="50%"></stop>
<stop stop-color="lime" offset="50%"></stop>
<stop stop-color="lime" offset="75%"></stop>
<stop stop-color="rgb(128,0,128)" offset="75%"></stop>
</linearGradient>
</defs>
<rect x="0" y="1" width="10" height="8" fill="url(#grad)" />
</svg>
''')
@assert_no_logs
def test_linear_gradient_repeat(assert_pixels):
assert_pixels('''

View File

@ -110,7 +110,7 @@ def draw_stacking_context(stream, stacking_context):
if box.style['opacity'] < 1:
original_stream = stream
stream = stream.add_group(stream.page_rectangle)
stream = stream.add_group(*stream.page_rectangle)
if box.transformation_matrix:
if box.transformation_matrix.determinant:
@ -391,7 +391,7 @@ def draw_background_image(stream, layer, image_rendering):
matrix @= stream.ctm
pattern = stream.add_pattern(
0, 0, image_width, image_height, repeat_width, repeat_height, matrix)
group = pattern.add_group([0, 0, repeat_width, repeat_height])
group = pattern.add_group(0, 0, repeat_width, repeat_height)
with stacked(stream):
layer.image.draw(group, image_width, image_height, image_rendering)

View File

@ -297,7 +297,7 @@ class Stream(pydyf.Stream):
super().set_state(key)
def set_alpha_state(self, x, y, width, height):
alpha_stream = self.add_group((x, y, width, height))
alpha_stream = self.add_group(x, y, width, height)
alpha_state = pydyf.Dictionary({
'Type': '/ExtGState',
'SMask': pydyf.Dictionary({
@ -330,7 +330,7 @@ class Stream(pydyf.Stream):
self._fonts[key] = Font(pango_font)
return self._fonts[key]
def add_group(self, bounding_box):
def add_group(self, x, y, width, height):
states = pydyf.Dictionary()
x_objects = pydyf.Dictionary()
patterns = pydyf.Dictionary()
@ -345,7 +345,7 @@ class Stream(pydyf.Stream):
extra = pydyf.Dictionary({
'Type': '/XObject',
'Subtype': '/Form',
'BBox': pydyf.Array(bounding_box),
'BBox': pydyf.Array((x, y, x + width, y + height)),
'Resources': resources,
'Group': pydyf.Dictionary({
'Type': '/Group',
@ -482,7 +482,7 @@ class Stream(pydyf.Stream):
extra = pydyf.Dictionary({
'Type': '/Pattern',
'PatternType': 1,
'BBox': pydyf.Array([x, y, width, height]),
'BBox': pydyf.Array([x, y, x + width, y + height]),
'XStep': repeat_width,
'YStep': repeat_height,
'TilingType': 1,

View File

@ -395,7 +395,7 @@ class SVG:
coords = (box[0], box[1], box[0] + box[2], box[1] + box[3])
else:
coords = (0, 0, self.inner_width, self.inner_height)
self.stream = self.stream.add_group(coords)
self.stream = self.stream.add_group(*coords)
# Apply transform attribute
self.transform(node.get('transform'), font_size)

View File

@ -106,8 +106,11 @@ def draw_gradient(svg, node, gradient, font_size, opacity, stroke):
return False
if gradient.get('gradientUnits') == 'userSpaceOnUse':
width, height = svg.inner_width, svg.inner_height
matrix = Matrix()
else:
width, height = bounding_box[2], bounding_box[3]
width, height = 1, 1
e, f, a, d = bounding_box
matrix = Matrix(a=a, d=d, e=e, f=f)
spread = gradient.get('spreadMethod', 'pad')
if spread in ('repeat', 'reflect'):
@ -131,39 +134,31 @@ def draw_gradient(svg, node, gradient, font_size, opacity, stroke):
positions.append(positions[-1] + 1)
colors.append(colors[-1])
if gradient.get('gradientUnits') == 'userSpaceOnUse':
matrix = Matrix()
else:
matrix = Matrix(a=width, d=height)
if gradient.tag == 'linearGradient':
shading_type = 2
x1, y1 = (
size(gradient.get('x1', 0), font_size, 1),
size(gradient.get('y1', 0), font_size, 1))
size(gradient.get('x1', 0), font_size, width),
size(gradient.get('y1', 0), font_size, height))
x2, y2 = (
size(gradient.get('x2', '100%'), font_size, 1),
size(gradient.get('y2', 0), font_size, 1))
size(gradient.get('x2', '100%'), font_size, width),
size(gradient.get('y2', 0), font_size, height))
positions, colors, coords = spread_linear_gradient(
spread, positions, colors, x1, y1, x2, y2, matrix)
else:
assert gradient.tag == 'radialGradient'
shading_type = 3
cx, cy = (
size(gradient.get('cx', '50%'), font_size, 1),
size(gradient.get('cy', '50%'), font_size, 1))
r = size(gradient.get('r', '50%'), font_size, 1)
size(gradient.get('cx', '50%'), font_size, width),
size(gradient.get('cy', '50%'), font_size, height))
r = size(gradient.get('r', '50%'), font_size, hypot(width, height))
fx, fy = (
size(gradient.get('fx', cx), font_size, width),
size(gradient.get('fy', cy), font_size, height))
fr = size(gradient.get('fr', 0), font_size, 1)
fr = size(gradient.get('fr', 0), font_size, hypot(width, height))
positions, colors, coords = spread_radial_gradient(
spread, positions, colors, fx, fy, fr, cx, cy, r, width, height,
matrix)
if gradient.get('gradientUnits') != 'userSpaceOnUse':
matrix @= Matrix(e=bounding_box[0], f=bounding_box[1])
matrix @= svg.stream.ctm
alphas = [color[3] for color in colors]
alpha_couples = [
(alphas[i], alphas[i + 1])
@ -183,18 +178,19 @@ def draw_gradient(svg, node, gradient, font_size, opacity, stroke):
if 0 not in (a0, a1) and (a0, a1) != (1, 1):
color_couples[i][2] = a0 / a1
x, y = 0, 0
x1, y1 = 0, 0
if 'gradientTransform' in gradient.attrib:
transform_matrix = transform(
gradient.get('gradientTransform'), font_size,
svg.normalized_diagonal)
x, y = transform_matrix.invert.transform_point(x, y)
width, height = transform_matrix.invert.transform_point(width, height)
matrix = transform_matrix @ matrix
x1, y1 = transform_matrix.invert.transform_point(x1, y1)
x2, y2 = transform_matrix.invert.transform_point(width, height)
width, height = x2 - x1, y2 - y1
pattern = svg.stream.add_pattern(
x, y, width, height, width, height, matrix)
group = pattern.add_group([x, y, width, height])
x1, y1, width, height, width, height, matrix @ svg.stream.ctm)
group = pattern.add_group(x1, y1, width, height)
domain = (positions[0], positions[-1])
extend = spread not in ('repeat', 'reflect')
@ -209,7 +205,7 @@ def draw_gradient(svg, node, gradient, font_size, opacity, stroke):
shading_type, 'RGB', domain, coords, extend, function)
if any(alpha != 1 for alpha in alphas):
alpha_stream = group.set_alpha_state(x, y, width, height)
alpha_stream = group.set_alpha_state(x1, y1, width, height)
domain = (positions[0], positions[-1])
extend = spread not in ('repeat', 'reflect')
encode = (len(colors) - 1) * (0, 1)
@ -447,7 +443,7 @@ def draw_pattern(svg, node, pattern, font_size, opacity, stroke):
matrix)
stream_pattern.set_alpha(opacity)
group = stream_pattern.add_group([0, 0, pattern_width, pattern_height])
group = stream_pattern.add_group(0, 0, pattern_width, pattern_height)
Pattern(pattern, svg).draw(
group, pattern_width, pattern_height, svg.base_url,
svg.url_fetcher, svg.context)