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

SVG node order issue #4179

Closed
drammock opened this issue Mar 2, 2015 · 6 comments
Closed

SVG node order issue #4179

drammock opened this issue Mar 2, 2015 · 6 comments

Comments

@drammock
Copy link

drammock commented Mar 2, 2015

Consider the following plot, which I created with version 1.5.dev1 (cloned and built in late January 2015):

import numpy as np
import matplotlib.pyplot as plt
data = np.arange(6).reshape(2, 3)
fig, sub = plt.subplots(1, 1, figsize=(4, 4))
bars = plt.bar(data[0], data[1])
_ = sub.set_ylim(1)
plt.savefig('test.svg')

If I open this SVG plot in firefox or in inkscape/inkview, it looks as expected. If I open it in the default Ubuntu image viewer (Eye of Gnome) or ImageMagick display, the bars are not properly clipped below the x-axis:
eyeofgnome

When using hatching patterns the problems multiply. This screenshot shows Firefox and Inkview in the top row, and ImageMagick and Eye of Gnome in the bottom row:
screenshot

I examined the SVG source, and found that in both cases there were one or more svg:defs nodes at the end of the document (i.e., after the svg:g node containing the main components of the figure). If I manually move those svg:defs nodes up so that they occur before the main figure svg:g node, then both files now display normally in all four viewers (only the second example file is shown here):
screenshot_after_reordering_nodes

I don't know much about the SVG specification, so I don't actually know if this is a case where matplotlib is following the rules and the viewers are to blame, or whether there is genuinely something wrong with the way matplotlib is generating the SVG, and some viewers (but not all) are clever enough to compensate for it. Regardless, it seems like reordering nodes before writing out the file might (?) be a fairly easy change to make, and would improve compatibility of matplotlib-authored SVGs.

I don't know if this last bit of information is relevant, but the problems with the unmodified SVGs persist if they are converted to EPS and then inserted into PDFs via LaTeX (when doing the conversion either with inkscape -f test.svg -E test.eps, or cairosvg test.svg -o test.ps; ps2eps -f -q -B test.ps). Again, some viewers (e.g., Adobe Reader on Windows) will display them correctly, but others (e.g., evince or muPDF) will not.

@mdboom
Copy link
Member

mdboom commented Mar 2, 2015

Yeah, I've long been aware of this issue, and assumed it would get fixed in librsvg eventually. There's nothing in the SVG spec that says the target of a "use" must appear before it is "used". So I'm pretty sure the matplotlib output is valid here. Also, at least the last I looked, which was a few years ago, there were a number of other shortcomings to the librsvg implementation (lack of filter support, probably others), that it didn't seem worth expending effort on workarounds for it when it was basically just an incomplete renderer anyway.

The problem is that the fix is not so easy -- we need to maintain the fact that the SVG file is produced as a stream (to reduce memory consumption and to be compatible with web servers etc.), and we don't know what the clip paths are until we've walked the tree (as we output it). So the only way to fix this would be to walk the tree silently first, finding all of the clip paths, output them, and then walk the tree while producing the output as we do now. I'd certainly merge something that did that if someone were inclined.

@drammock
Copy link
Author

drammock commented Mar 2, 2015

That change is probably beyond my abilities... I browsed the source tree a bit but couldn't find where the SVG writing actually happens (I got as far as finding self.canvas.print_figure in figure.py, but I'm guessing from there it depends on the backend). Feel free to close this issue since it's not really a matplotlib problem, unless you want to leave it open as bait for other more capable contributors.

@tacaswell tacaswell added this to the unassigned milestone Mar 2, 2015
@mdboom
Copy link
Member

mdboom commented Mar 2, 2015

It's probably worth leaving open -- it would be nice to fix eventually, I just consider it low priority.

To answer you question, the SVG writing happens in backends/backend_svg.py.

@tacaswell
Copy link
Member

Duplicate of #4341, closing in favor of that one.

@mdboom
Copy link
Member

mdboom commented Jan 12, 2016

It looks like librsvg 2.40.12 fixes this bug!!! That was a long time coming. Glad we stuck to our guns! The relevant entry in the librsvg CHANGELOG:

  • References to objects/filters/URIs/etc. are now handled lazily. Also, there is a general-purpose cycle detector so malformed SVGs don't cause infinite loops. Work by Benjamin Otte.

Benjamin Otte: beverage of your choice if I ever meet you. This is a big thing.

@drammock
Copy link
Author

huzzah!

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

No branches or pull requests

3 participants