使用 librsvg 和 Python 渲染 SVG 时的差异
Differences while rendering SVG with librsvg and Python
根据将 SVG 渲染为整个文档或单个元素显示渲染差异。
我使用 Inkscape 创建了一个简单的 SVG 图形并想使用 Python 渲染它。我决定 librsvg 是要走的路。这是我的 SVG,从 Inkscape 保存为“普通 SVG”(没有特定于 Inkscape 的扩展)。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="23.105469mm"
height="23.10545mm"
viewBox="0 0 23.105469 23.10545"
version="1.1"
id="svg1380"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1377">
<radialGradient
xlink:href="#SphereBlueGlow"
id="radialGradient17266-1-5-3"
cx="206.91444"
cy="205.5472"
fx="206.91444"
fy="205.5472"
r="11.552734"
gradientTransform="translate(-188.33616,-3.103272)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="SphereBlueGlow">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop954-7" />
<stop
style="stop-color:#44ccff;stop-opacity:0"
offset="0.69538838"
id="stop956-4" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop958-1" />
</linearGradient>
<radialGradient
xlink:href="#SphereSpecularReflection"
id="radialGradient868-5-1-3-3-5-3-5-8-5-2-9-0-9-3-9-2-0-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.11379011,0.15082671,-0.14646196,0.11049697,33.91806,171.06396)"
cx="60.713989"
cy="169.90594"
fx="60.713989"
fy="169.90594"
r="37.436264" />
<linearGradient
id="SphereSpecularReflection">
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0"
id="stop944" />
<stop
style="stop-color:#8e8e8e;stop-opacity:0"
offset="0.37699515"
id="stop946" />
<stop
style="stop-color:#000000;stop-opacity:0.02313977"
offset="1"
id="stop948" />
</linearGradient>
</defs>
<g
id="layer1"
transform="translate(3.4848234,-128.62724)">
<g
id="zauberplatzPassiv"
transform="translate(-10.51038,-62.263795)">
<ellipse
style="mix-blend-mode:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-3-7-9-8-0-7-1-4-0-2-1-9-9-7-6-44-5-9"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
<ellipse
style="mix-blend-mode:normal;fill:url(#radialGradient17266-1-5-3);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-3-7-9-8-0-7-1-4-0-2-1-9-9-7-6-4-6-5-2"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
<ellipse
style="mix-blend-mode:hard-light;fill:url(#radialGradient868-5-1-3-3-5-3-5-8-5-2-9-0-9-3-9-2-0-2);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-1-7-1-6-8-3-7-2-0-6-1-8-0-8-4-0-9-0"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
</g>
</g>
</svg>
我的 Python 渲染代码(希望它能在这个问题的缩减中幸存下来):
import gi
gi.require_version('Rsvg', '2.0')
from gi.repository import Rsvg
import cairo
def render_image(svg):
ratio = svg.props.em / svg.props.dpi_x
svg.set_dpi(160 / ratio)
dim = svg.get_dimensions()
# create the cairo context
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, dim.width, dim.height)
context = cairo.Context(surface)
svg.render_cairo(context)
surface.write_to_png('sphere_used.image.png')
def render_elements (svg):
rect = Rsvg.Rectangle()
rect.x = 0
rect.y = 0
rect.width = 160
rect.height = 160
# create the cairo context
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(rect.width), int(rect.height))
context = cairo.Context(surface)
svg.render_element(context, '#zauberplatzPassiv', rect)
surface.write_to_png('sphere_used.element.png')
if __name__ == '__main__':
# use rsvg to render the cairo context
svg = Rsvg.Handle().new_from_file(INPUTFILE)
render_image(svg)
render_elements(svg)
render_image(svg)
的输出似乎与我在 Inkscape 中所做的相同:
相比之下,render_element(svg)
的输出错过了镜面反射:
显然,librsvg 支持所有使用的 SVG 功能,并且能够正确显示图像。 But when selecting a single element, there's something going wrong. 我可以在 SVG 或 Python 方面做些什么来让它们看起来一样吗?
我可以想象在这两种情况下应用不同 SVG 转换的顺序是不同的,导致反射在屏幕外渲染。但我不知道 SVG 是否足够好。
罪魁祸首是mix-blend-mode:hard-light;
。
我清理了 SVG,重置了所有翻译,但高亮部分仍然丢失。只有将 mix-blend-mode
从 hard-light
设置为 normal
后,它才重新出现。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="160px"
height="160px"
viewBox="0 0 160 160"
version="1.1"
id="svg1380"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1377">
<radialGradient
xlink:href="#SphereBlueGlow"
id="circular_glow"
cx="80"
cy="80"
fx="80"
fy="80"
r="80"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="SphereBlueGlow">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop954-7" />
<stop
style="stop-color:#44ccff;stop-opacity:0"
offset="0.69538838"
id="stop956-4" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop958-1" />
</linearGradient>
<radialGradient
xlink:href="#SphereSpecularReflection"
id="circular_specular_reflection"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.65,0,0,0.65,10,5)"
cx="80"
cy="80"
fx="80"
fy="80"
r="80" />
<linearGradient
id="SphereSpecularReflection">
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0"
id="stop000" />
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0.05"
id="stop040" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0.5"
offset="0.14"
id="stop060" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0.1"
offset="0.2"
id="stop080" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0"
offset="0.3"
id="stop020" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0"
offset="1"
id="stop100" />
</linearGradient>
</defs>
<g
id="layer1">
<g
id="zauberplatzPassiv">
<ellipse
style="mix-blend-mode:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="black-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
<ellipse
style="mix-blend-mode:normal;fill:url(#circular_glow);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="glow-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
<ellipse
style="mix-blend-mode:normal;fill:url(#circular_specular_reflection);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="highlight-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
</g>
</g>
</svg>
我不得不编辑渐变以使其更适合这种混合模式,但结果对我来说已经足够好了:
我仍然很好奇 hard-light
有什么特别之处,所以如果有任何 librsvg 专家出现,他们也许可以阐明它。
根据将 SVG 渲染为整个文档或单个元素显示渲染差异。
我使用 Inkscape 创建了一个简单的 SVG 图形并想使用 Python 渲染它。我决定 librsvg 是要走的路。这是我的 SVG,从 Inkscape 保存为“普通 SVG”(没有特定于 Inkscape 的扩展)。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="23.105469mm"
height="23.10545mm"
viewBox="0 0 23.105469 23.10545"
version="1.1"
id="svg1380"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1377">
<radialGradient
xlink:href="#SphereBlueGlow"
id="radialGradient17266-1-5-3"
cx="206.91444"
cy="205.5472"
fx="206.91444"
fy="205.5472"
r="11.552734"
gradientTransform="translate(-188.33616,-3.103272)"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="SphereBlueGlow">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop954-7" />
<stop
style="stop-color:#44ccff;stop-opacity:0"
offset="0.69538838"
id="stop956-4" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop958-1" />
</linearGradient>
<radialGradient
xlink:href="#SphereSpecularReflection"
id="radialGradient868-5-1-3-3-5-3-5-8-5-2-9-0-9-3-9-2-0-2"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.11379011,0.15082671,-0.14646196,0.11049697,33.91806,171.06396)"
cx="60.713989"
cy="169.90594"
fx="60.713989"
fy="169.90594"
r="37.436264" />
<linearGradient
id="SphereSpecularReflection">
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0"
id="stop944" />
<stop
style="stop-color:#8e8e8e;stop-opacity:0"
offset="0.37699515"
id="stop946" />
<stop
style="stop-color:#000000;stop-opacity:0.02313977"
offset="1"
id="stop948" />
</linearGradient>
</defs>
<g
id="layer1"
transform="translate(3.4848234,-128.62724)">
<g
id="zauberplatzPassiv"
transform="translate(-10.51038,-62.263795)">
<ellipse
style="mix-blend-mode:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-3-7-9-8-0-7-1-4-0-2-1-9-9-7-6-44-5-9"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
<ellipse
style="mix-blend-mode:normal;fill:url(#radialGradient17266-1-5-3);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-3-7-9-8-0-7-1-4-0-2-1-9-9-7-6-4-6-5-2"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
<ellipse
style="mix-blend-mode:hard-light;fill:url(#radialGradient868-5-1-3-3-5-3-5-8-5-2-9-0-9-3-9-2-0-2);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="path833-1-7-1-6-8-3-7-2-0-6-1-8-0-8-4-0-9-0"
cx="18.578291"
cy="202.44376"
rx="11.552734"
ry="11.552725" />
</g>
</g>
</svg>
我的 Python 渲染代码(希望它能在这个问题的缩减中幸存下来):
import gi
gi.require_version('Rsvg', '2.0')
from gi.repository import Rsvg
import cairo
def render_image(svg):
ratio = svg.props.em / svg.props.dpi_x
svg.set_dpi(160 / ratio)
dim = svg.get_dimensions()
# create the cairo context
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, dim.width, dim.height)
context = cairo.Context(surface)
svg.render_cairo(context)
surface.write_to_png('sphere_used.image.png')
def render_elements (svg):
rect = Rsvg.Rectangle()
rect.x = 0
rect.y = 0
rect.width = 160
rect.height = 160
# create the cairo context
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(rect.width), int(rect.height))
context = cairo.Context(surface)
svg.render_element(context, '#zauberplatzPassiv', rect)
surface.write_to_png('sphere_used.element.png')
if __name__ == '__main__':
# use rsvg to render the cairo context
svg = Rsvg.Handle().new_from_file(INPUTFILE)
render_image(svg)
render_elements(svg)
render_image(svg)
的输出似乎与我在 Inkscape 中所做的相同:
相比之下,render_element(svg)
的输出错过了镜面反射:
显然,librsvg 支持所有使用的 SVG 功能,并且能够正确显示图像。 But when selecting a single element, there's something going wrong. 我可以在 SVG 或 Python 方面做些什么来让它们看起来一样吗?
我可以想象在这两种情况下应用不同 SVG 转换的顺序是不同的,导致反射在屏幕外渲染。但我不知道 SVG 是否足够好。
罪魁祸首是mix-blend-mode:hard-light;
。
我清理了 SVG,重置了所有翻译,但高亮部分仍然丢失。只有将 mix-blend-mode
从 hard-light
设置为 normal
后,它才重新出现。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="160px"
height="160px"
viewBox="0 0 160 160"
version="1.1"
id="svg1380"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1377">
<radialGradient
xlink:href="#SphereBlueGlow"
id="circular_glow"
cx="80"
cy="80"
fx="80"
fy="80"
r="80"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="SphereBlueGlow">
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="0"
id="stop954-7" />
<stop
style="stop-color:#44ccff;stop-opacity:0"
offset="0.69538838"
id="stop956-4" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop958-1" />
</linearGradient>
<radialGradient
xlink:href="#SphereSpecularReflection"
id="circular_specular_reflection"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.65,0,0,0.65,10,5)"
cx="80"
cy="80"
fx="80"
fy="80"
r="80" />
<linearGradient
id="SphereSpecularReflection">
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0"
id="stop000" />
<stop
style="stop-color:#e6e6e6;stop-opacity:1"
offset="0.05"
id="stop040" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0.5"
offset="0.14"
id="stop060" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0.1"
offset="0.2"
id="stop080" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0"
offset="0.3"
id="stop020" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0"
offset="1"
id="stop100" />
</linearGradient>
</defs>
<g
id="layer1">
<g
id="zauberplatzPassiv">
<ellipse
style="mix-blend-mode:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="black-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
<ellipse
style="mix-blend-mode:normal;fill:url(#circular_glow);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="glow-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
<ellipse
style="mix-blend-mode:normal;fill:url(#circular_specular_reflection);fill-opacity:1;stroke:none;stroke-width:0.155336;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.529412"
id="highlight-ellipse"
cx="80"
cy="80"
rx="80"
ry="80" />
</g>
</g>
</svg>
我不得不编辑渐变以使其更适合这种混合模式,但结果对我来说已经足够好了:
我仍然很好奇 hard-light
有什么特别之处,所以如果有任何 librsvg 专家出现,他们也许可以阐明它。