Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Masked Image element (and possibly other elements) translates the mask against the SVG standard. #365

Open
dunaden001 opened this issue Aug 14, 2022 · 2 comments
Labels

Comments

@dunaden001
Copy link

dunaden001 commented Aug 14, 2022

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 without any translation applied to the mask.

Below I will paste a cut down SVG example, this is a link to the expected rendering and this is a link to how cairosvg renders it. You can see in the cairosvg render that the image is cut off 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.

<?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>

Note that I first asked this as a question on StackOverflow and user Robert Longson pointed me to this reference which states that the default value of the mask attribute (maskContentUnits) is "userSpaceOnUse" and this indicates:

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).

This seems to support that the behavior shown by cairosvg in this case is against the standard and it should be noted that explicitly setting the maskContentUnits has no effect on the output from cairosvg (while setting it to "objectBoundingBox" does change the output in other renderers like Chrome and Inkscape).

@liZe liZe added the bug label Aug 15, 2022
@ValentinFrancois
Copy link

I think I have the same issue.

I'm using a common masking technique to draw contours only inside/only outside a shape.
It renders fine in web browsers (at least Chrome and Firefox), but svg2png doesn't seem to apply my mask correctly.

I'm uploading the SVG source and the PNG output from svg2png

SVG:
test

PNG:
test

As you should see in the browser rendering this comment, the PNG has a contour thickness twice as big as the SVG contour thickness.

I am indeed using maskUnits="userSpaceOnUse" (my intention is to express the coordinates of the shapes in the mask relative to the same reference as the coordinates of the shapes outside of the mask, i.e. the top left corner of the SVG viewbox):

<mask height="100%"  width="100%" x="0" y="0"  maskUnits="userSpaceOnUse">
    <polygon fill="white" points="..."/>
    <polygon fill="black" points="..."/>
</mask>

@MichaelAReed I'm not sure I fully understood your explanation but it sounds like I could just apply a translation to the polygon points inside the mask to solve my problem, right? Since I generate these in a script I can live with a workaround instead of a fix from CairoSVG.

@dunaden001
Copy link
Author

I'm not really following how the first section of your post about the contour thickness leads onto the second section asking about setting maskUnits. It appears that the issue between the images you posted is related to a stroke-width parameter in the black polygon of your mask. If you go and change that stroke-width to 0, I think the svg2png output is as you are expecting according to the chrome rendering.

I'm not familiar enough with the svg standard to give advice on which of the 2 are actually following the standard (ie. is using the stroke-width in the mask a bug in cairosvg or is not using it a bug in Chrome) but if you only care about converting your svg internally and don't need to separately display the svg to users through a browser, then you should be fine to follow along with whatever cairosvg does (as long as you are also happy to either freeze the version or update your scripts if cairosvg does eventually update).

The maskUnits default is "userSpaceOnUse" and I don't think it's correlated to your issue at all but feel free to correct me if I'm missing something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants