Home > Net >  PNG export in cairosvg with masked Image element appears to violate SVG standard
PNG export in cairosvg with masked Image element appears to violate SVG standard

Time:08-14

Background: I've written some python code (using svgwrite) to generate a set of SVGs and I'm trying to find a (preferably purely python) method of converting these SVGs to PNGs. I'm trying to use svg2png from the cairosvg library but I'm running into a few issues, one of which I'm distilling out for this question.

Problem: The issue seems to be with how cairosvg interprets the parameters of an Image element with a mask. It seems that when you provide an 'x' and 'y' parameter to the Image element, cairosvg first applies the mask as if both parameters were 0 and then after the mask is applied, translates the result according to the given values of 'x' and 'y'. How I believe it should be interpreted according to the specs (and how Chrome, Inkscape and various other rendering engines interpret it) is that the image is translated and then the mask applied to it afterward.

Below I will paste a cut down example of SVG code (note that it references an external png file, this can be substituted for any other png larger than 200x200px), the expected rendering and how cairosvg renders it. You can see in the cairosvg render that the image is cutoff to quarter-size because the image corner is placed at the center of the image when the mask is applied. It is also offset from the expected location because the result is translated after masking.

Request: I am looking for someone to confirm that I have misunderstood how the SVG standard is meant to interpret this situation, that cairosvg has a bug that I should report or some other situation I'm not seeing at all. Additionally, if it is a bug with cairosvg, any workarounds that can force the correct output would be appreciated.

<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" baseProfile="full" height="100%" version="1.1" viewBox="-50,-50,100,100" width="100%">
<mask id="outline_mask">
    <rect fill="white" height="33" rx="0" ry="0" stroke-width="0" width="45" x="-22.5" y="-16.5"/>
</mask>
<image height="100" mask="url(#outline_mask)" preserveAspectRatio="xMidYMid slice" width="200" x="-100" xlink:href="https://besthqwallpapers.com/Uploads/27-3-2019/85029/thumb2-old-paper-texture-4k-paper-background-paper-textures-old-paper.jpg" y="-50"/>
<rect fill="black" height="20" rx="0" ry="0" stroke-width="0" width="32" x="-16" y="-10"/>
</svg>

Incorrect Rendering from cairosvg Correct rendering (from Chrome), please ignore the size of whitespace

Edit: For some clarification, I have also tried using svglib which seems to yield even more but different issues with this simple example and my full SVG files. If you're interested, this is the result of exporting the same file with libsvg which appears to simply not implement the mask at all.

CodePudding user response:

The default for maskContentUnits if not specified is userSpaceOnUseUnits. That means the mask uses the same co-ordinate system as the image.

If maskContentUnits="userSpaceOnUse", the user coordinate system for the contents of the ‘mask’ element is the current user coordinate system in place at the time when the ‘mask’ element is referenced (i.e., the user coordinate system for the element referencing the ‘mask’ element via the ‘mask’ property).

The image's x and y don't change its co-ordinate system like a transform attribute would so when you move the image by changing its x and y attributes, the mask should stay where it is.

CodePudding user response:

pip install svglib

>>> from svglib.svglib import svg2rlg
>>> from reportlab.graphics import renderPDF, renderPM
>>>
>>> drawing = svg2rlg("file.svg")
>>> renderPDF.drawToFile(drawing, "file.pdf")
>>> renderPM.drawToFile(drawing, "file.png", fmt="PNG")
  • Related