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

Matplotlib: savefig produces incorrect SVG image for bar chart with log-scaled Y-axis #4341

Closed
abreslow opened this issue Apr 15, 2015 · 13 comments

Comments

@abreslow
Copy link

See my stackoverflow post: http://stackoverflow.com/questions/29616764/matplotlib-savefig-produces-incorrect-svg-image-for-bar-chart-with-log-scaled-y

A potential solution to the issue is described here: ipython/ipython#8133 (comment)

@tacaswell
Copy link
Member

90% this is a veiwer issue, not a MPL issue.

@tacaswell tacaswell added this to the unassigned milestone Apr 15, 2015
@WeatherGod
Copy link
Member

True, but it does seem fairly trivial to fix, though. Just write out the
clipping path earlier, IIUC. We can't fight the whole world.

On Wed, Apr 15, 2015 at 2:12 PM, Thomas A Caswell notifications@github.com
wrote:

90% this is a veiwer issue, not a MPL issue.


Reply to this email directly or view it on GitHub
#4341 (comment)
.

@mdboom
Copy link
Member

mdboom commented Apr 15, 2015

Yes. This is a long-known issue in librsvg-based viewers. The SVG spec
allows for clip paths defined after their reference. Only librsvg gets
this wrong.

@WeatherGod: Sorry, it's not a trivial fix because it means we have to go
from stream-based writing (which is important for web servers and
minimizing memory usage) to random-access (or at least multi-pass) writing.

On Wed, Apr 15, 2015 at 2:12 PM Thomas A Caswell notifications@github.com
wrote:

90% this is a veiwer issue, not a MPL issue.


Reply to this email directly or view it on GitHub
#4341 (comment)
.

@tacaswell
Copy link
Member

I just checked and this renders correctly in both chromium and firefox.

@WeatherGod
Copy link
Member

@mdboom, forgive my ignorance on the particulars of the svg backend, but
what is the limitation that forces us to write the clipping path
definitions after all of their references? Do we need to write out the
other artists first to get the clip or something? Just curious.

On Wed, Apr 15, 2015 at 2:16 PM, Michael Droettboom <
notifications@github.com> wrote:

Yes. This is a long-known issue in librsvg-based viewers. The SVG spec
allows for clip paths defined after their reference. Only librsvg gets
this wrong.

@WeatherGod: Sorry, it's not a trivial fix because it means we have to go
from stream-based writing (which is important for web servers and
minimizing memory usage) to random-access (or at least multi-pass) writing.

On Wed, Apr 15, 2015 at 2:12 PM Thomas A Caswell <notifications@github.com

wrote:

90% this is a veiwer issue, not a MPL issue.


Reply to this email directly or view it on GitHub
<
#4341 (comment)

.


Reply to this email directly or view it on GitHub
#4341 (comment)
.

@ghost
Copy link

ghost commented Apr 18, 2015

@mdboom Can you provide the part of the SVG specification that talks about the position of the clipping paths or defs blocks? I haven't been able to find that.

@tacaswell
Copy link
Member

@rsmith31415 http://www.w3.org/TR/SVG/struct.html#SVGElement It says the svg element may contain any number of any (of a long list) of elements types in any order.

The section of defs http://www.w3.org/TR/SVG/struct.html#DefsElement says:

To provide some SVG user agents with an opportunity to implement efficient implementations in streaming environments, creators of SVG content are encouraged to place all elements which are targets of local IRI references within a ‘defs’ element which is a direct child of one of the ancestors of the referencing element.

which we are doing (assuming I am understanding 'one of the ancestors' correctly to include the root node).

I suspect that this is the reason that we emit all of the clip paths at the end. The clip urls are shared with in a figure (which is the correct thing to do). The first time we see each clip path it is generated and stashed for later re-use. If the <def> block always needs to be a child on an ancestor of the element, it is used in then it is not safe to emit the clip path where you first see it (as there may be less nested use of it later) and we don't know what the clip paths will be until we walk the full artist tree once.

Conversely, there is no other explicit requirement that it must come before so it is allowed to come after.

It appears that this got reported upstream to ubuntu (https://bugs.launchpad.net/ubuntu/+source/librsvg/+bug/1207538) but was never bumped up to actual upstream (which I just fixed as https://bugzilla.gnome.org/show_bug.cgi?id=748565).

I am closing this as this is pretty clearly a bug in librsvg as the svgs render correctly in:

  • firefox,
  • chromium (don't have chrome on this box to test, but others report it works
  • inkscape
  • rekonq
  • konqueror
  • imagemagick (at least as rendered by emacs, via display, and via convert)

there is nothing in the spec which says this can't be done, and there are major benefits to doing svg writing in a streaming fashion.

The emitted svg render badly with 2 svg libraries (librsvg and libQtSvg)

  • gimp (probably librsvg, which it has a dependency on)
  • karbon (libQtSvg)
  • gnome image viewer (probably ligrsvg based)
  • kolor paint (libQtSvg)

due to upstream bugs.

@tacaswell
Copy link
Member

And the failure to render with libQtSvg is expected as it only supports a very minimal set of svg (SVG 1.2 tiny) which it is my understanding does not support clipping.

@ghost
Copy link

ghost commented Apr 28, 2015

@tacaswell Yes, I read that part and to be fair, it simply doesn't say anything about the position (which by omission, you might think that it shouldn't matter.) Maybe the confusion arises from the fact that virtually all examples of SVG code place <defs> at the top.

The problem is that SVG is really important and this issue affects a sizable number of users, so it would be nice to have a solution, if possible of course. I'm not sure about the specifics, but wouldn't it be possible to read the SVG elements as you're already doing and before outputting the resulting element, move the <defs> element to the top. Keep in mind that matplotlib is already producing SVGs with two <defs> (one at the bottom and one at the top), so maybe cleaning a bit that part would be a good idea, but as I said, as a post-processing step.

@abreslow
Copy link
Author

@rsmith31415 and @tacaswell, I guess I'll just write a script to do it. It's far from ideal but is probably the best option at this point.

It would be nice if there was a fix to matplotlib for this but doing so would violate the end-to-end principle, so maybe that's not the best approach. However, practically speaking, how long is going to take to get it fixed in librsvg and widely deployed? It seems like applying a workaround in Matplotlib is likely to solve the problem faster for Linux users, so that is the trade-off more or less.

@tacaswell
Copy link
Member

I have no idea how responsive the librsvg developers are or even how big of a fix this is for them. The bug has been known for a while, but apparently in never made it upstream until last night.

@abreslow If you want to make that a function to put into matplotlib.cbook I think that would be useful, call it something like fix_svg_for_librsvg(fin, fout).

@ghost
Copy link

ghost commented Apr 28, 2015

@abreslow Thanks! It would be great to have a workaround for this.

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

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

4 participants