This patch addresses the issue #353. Some backends could really benefit from the text alignment information that is present in mpl.text.Text instances. For instance, editing a pdf or svg would be much easier if the text elements were anchored as intended.
In contrast to the documentation of RendererBase.draw_text, a backend only receives a simple string instead of a Text instance. The patch simply adds the optional "mtext" kwarg that is used to propagate Text instances to the backends without changing the way text rendering is handled in any way. I also ran the tests from tests.py and everything seems to be fine.
This is a good idea -- and yes, that's a totally bogus docstring. I think before merging this, however, I'd like to see it developed to the point of actually using the text object within the backend to see how that plays out and all the details make sense. (It could just be on one backend for now). One thing that will have to change is that the (x, y) sent to the backend needs to become the anchor point, not always the lower left as it is now.
My idea was that the current way of text handling is not to be interfered with. That means the arguments (x, y) still point to the position of the lower left as calculated by matplotlib. If you want to (optionally) anchor it yourself just lookup (x, y, va, ha) from mtext where (x, y) points to the (va, ha) anchor.
I was wondering why the mtexts were not given to the backends as documented in the first place, but you are doing some latex related filtering of the string as it seems. Also, if someone implements an artist that wants to layout and draw a lot of text elements he could do so by calling draw_text without any of the texts existing as mtext instances. So it's probably a good thing to only use this as an option :)
For demonstration I could re-enable anchoring support in the pgf backend. I did a workaround once by walking through the figure text instances which was rather painful due to text elements popping up that shouldn't have been rendered. The anchoring was working with the exception of rotated texts. This would take a little bit more time to implement because matplotlib interprets anchors and rotations differently than tikz/pgf. Nevertheless, it was a huge improvement since all texts were perfectly aligned even when changing the fonts afterwards.
I agree that this seems like the right approach to get text alignment information to the backend, that avoids some of the issues you saw with other approaches.
Yes -- the PGF backend would be a great test case of this. The anchoring of angled text is like nothing I've seen anywhere outside of matplotlib -- it's also a mismatch for PDF and SVG etc., but that's a tough thing to change without breaking backward compatibility. Whatever calculation on the anchor point that needs to be performed should perhaps be added to the Text object itself so that all backends can share that code.
I'd like to add support for this in SVG, PDF and PS as well (in that order, I think, because SVG being the most easily editable, it's really important there.) Hopefully between the two of us we can at least get SVG done before the freeze. I think that has the potential to be a very compelling feature for a lot of people.
This closes #353.
Rotated text is a bit tough indeed. My problem at first was to figure out the behavior in matplotlib. From what I've seen it is the axis aligned bounding box of a rotated text that is being aligned. For tikz/pgf the text anchor point is also the pivot point for rotations, but bounding box alignment must be done manually I'm afraid :/. Both definitions have their drawbacks. I don't know how SVG and PDF define the pivot points for rotations with respect to text anchors.
Maybe it's a good idea to take a step back and collect information about which pivot point definitions are easiest to support in SVG, PDF, PS and PGF. For pgf/tikz aligning text to pivot point - easy, aligning rotated bounding box - ugly but possible.
In any case I think merging this PR will make it more easy to experiment with text-align support + there are no drawbacks. The worst thing that could happen is that at some point when implementing anchoring it could appear favorable to redefine the definition of pivot points ^^.
This will produce a SVG where no-math-not-rotated text elements are horizontally anchored. Unfortunately I found a major show-stopper concerning SVG. Nobody seems to implement vertical text alignment :(. Chrome has some support, but no luck with inkscape cairo firefox etc.
That's a bummer about the SVG vertical text alignment support. But I think it's still most useful to have the horizontal text alignment working. The most common use case is probably editing the title and having the text stay centered -- that works in this scenario, so it's still a win.
I think this is probably ready to merge -- but we should create a new issue for the fact that PDF and PS don't use the alignment information directly.
Could you merge the PGF backend first? The mtext kwarg must be added to all backends, so once backend_pgf is merged to master I'll modify this PR and it will be ready to merge too.
Sure. The PGF stuff should probably get merged today.
All right, I fixed up the first commit so it includes backend_pgf as well and re-ran all tests. The PR should be good to go.
Just a note. The "rotation_mode"attribute of Text is meant to make the given position as an anchoring point of rotation.
That's perfect, thanks leejjjoon! One could check that property and support anchored rotation then.
The alignment of text elements is now included in the pgf output. When switching fonts within TeX documents, the texts are staying aligned nicely regardless of the new font metrics.
The SVG backend now also aligns anchor-rotated text elements.
Due to the lack of 'alignment-baseline' support in most applications only the left/right/center alignment is done in SVG, the position in the vertical direction of the text is fixed. By the way, backend_svg did not handle the font decent for anchor-rotated text correctly. This is fixed now.
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.text(2, 2, "- xXgXx -", rotation=45, rotation_mode="anchor",
This is the output of the code. You should be able to edit the y-label and a 45° rotated text in inkscape with center alignment now:
Finally I a had a chance to test this out. The new behavior of the svg is a huge improvement and it is really cool
to be able to change the text directly within a latex document and having the text staying nicely aligned in the plot.
Merge as a new feature to master?
@mdboom , @leejjoon Is this good to merge?
I think this is cool enough for a "What's New" entry and probably should go in the CHANGELOG as well. The existing SVG tests should be sufficient to test this. Other than that, looks good to me.
propagate mpl.text.Text instances to backends, fix documentation
backend_svg: horizontal text alignment for anchored rotations
use anchored rotation for y-labels
backend_pgf: implement anchoring of text elements
add text alignment feature to what's new
I applied the suggested changes. The travis setup for python3 seems to be broken at the moment, local testing succeeded however.
Cool. The Travis issue looks like this one numpy/numpy#2761
fix issue #1572 caused by PR #1081