SVG transform-origin ignored
The attribute "transform-origin" sets the reference point used in transformations set by the "transform" attribute. In the examples below, a rotation was used with the origin set to the center of the triangle. In WeasyPrint, the origin is ignored and the triangle is rotated relative to (0, 0). When the object is far from (0, 0), it can rotate far from it's intended location and far outside the viewport.
The following SVG reproduces the problem. The rotation is small in order to make the error more visible.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<defs>
<g id="triangle" transform="scale(0.6)">
<path d="M 0,-20 l 23,40 l -46,0 Z" stroke="purple" fill="none" />
</g>
</defs>
<use href="#triangle" x="10mm" y="10mm"/>
<use href="#triangle" x="10mm" y="10mm" transform-origin="10mm 10mm" transform="rotate(9)" />
</svg>
In Firefox, the triangle is rotated relative to the center of the triangle:
But WeasyPrint rotates it relative to (0, 0). It this example it looks slightly offset because the rotation is small.
Reference files: svgtransformorigin-firefox.pdf svgtransformorigin.pdf
Thanks for the bug report.
It is supported by browsers, but it’s not in the SVG specification, even in SVG2. It’s in this draft, overridden by this CSS module.
If anyone is interested in adding this feature, we need to update this function (and the utils.transform function it calls) to take care of transform-origin:
https://github.com/Kozea/WeasyPrint/blob/a5d8412d0f11e815db31652fdd88fed2fecfeb66/weasyprint/svg/init.py#L748-L755
Hi, is this still available for me to work on?
Hi, is this still available for me to work on?
Yes, of course! Don’t hesitate to ask if you have any questions.
From what I can tell, SVG.transform() is only called from within draw_node() in the same file, where it applies the transform attribute:
self.transform(node.get("transform"), font_size)
I can also use node.get("transform-origin") to get "10mm 10mm".
Is there anywhere in code that actually allows setting the origin based on this value?
I know the browser is able to read this and treat it as CSS but WeasyPrint is based on SVG specs which ignores transform-origin.
Do you think the solution for this is simple? Or does it require modifying the matrix logic? I'm not too familiar with how everything is computed within the utilt.py transform function.
Anymore relevant context would be greatly appreciated.
Is there anywhere in code that actually allows setting the origin based on this value?
You’re on the right track!
What you can do is give node instead of node.get('transform') to SVG.transform. In SVG.transform, you can get the transform attribute (with node.get('transform')) as transform_string and give it to the utils.transform function (it’s already done). You can also get the transform-origin and give it to utils.transform using a new origin_string parameter.
In utils.transform, what you can do is parsing the origin_string value to get the two sizes. You can first try with "simple" cases, where there are two values with units (like 10mm 10mm). Split the string to get the two 10mm strings, and transform them into origin_x, origin_y numbers using the size function. You can then transform the matrix created at the top of utils.transform by replacing matrix = Matrix() by matrix = Matrix(e=origin_x, f=origin_y).
When simple values work, we’ll be able to handle more complex cases. You can open a draft pull request to share your code, we’ll talk there. Don’t hesitate to ask if my explanations are not clear, it’s easy to get lost, but it’s rewarding to get everything working well at the end!
I’ve opened a draft pull request here: #2466