From: Craig Lang [mailto:firstname.lastname@example.org]
Sent: Monday, October 26, 2009 13:04
I am using matplotlib to generate an SVG plot containing a mixture of Annotations and Circles. I noticed that the annotation text does not appear at exactly the correct location when outputting to SVG. The difference is minor, but definitely present.
The following will reproduce the problem in the form of two files, an svg and a png:
Further investigation reveals that this problem occurs with ps and pdf output as well. It seems that all backend_*.py files in /usr/share/pyshared/matplotlib/backends suffer from this problem. I have poked around in a few files but can't see any obvious fixes.
Has anyone encountered this problem before and found a decent workaround?
(I'm cc'ing the development list.)
I believe I have some understanding of what's happening. The backends you mentioned use routines in ft2font.cpp to align text. The algorithms for aligning text use information returned by the function compute_string_bbox, which bases the bounding box on the extent of the painted regions of the glyphs. The width and height of that box are computed by get_width_height (also in ft2font.cpp) and returned to the renderer, which hands them off to the _get_layout method of each text object. That method leaves the anchor point (near the lower-left corner of the text) undisturbed for left-aligned text, but for centered or right-aligned text shifts it left by half or all, respectively, of the bounding box width. The resulting coordinate is returned to the text object's draw method, which eventually calls the renderer.
The difference arises in how the renderers for the different backends treat the anchor coordinate. The bitmap Agg backend uses draw_glyphs_to_bitmap in ft2font.cpp, and I think that that function aligns the leftmost ink of the bitmapped text to the anchor point. Because the anchor point was adjusted, if at all, by the width of the inked area, it's the inked area of the text that is left-, center-, or right-aligned. In contrast, the SVG, PS, and PDF backends make text objects at that anchor coordinate in their output. (I'm glossing over more complex cases like that of text converted to paths). However, the inked area of the first character may be to the right (as with your H) or to the left (as often with lowercase j) of the anchor point. (See, for example, http://www.tortall.net/mu/wiki/CairoTutorial#understanding-text.) If the text is to be center- or right-aligned, the anchor point has been adjusted only for the width of the inked area, so any offset of the ink relative to the initial anchor is simply translated to the other alignments. Thus, your H was too far to the right.
I showed some different manifestations of this behavior in a tracker I filed last year, at http://sourceforge.net/tracker/?func=detail&atid=560721&aid=1978234&group_id=80706. The digits and decimal points of the y-axis tick labels are out of alignment, the x-axis tick labels have different baselines, and the numbers in the middle are not aligned in columns (although in PDF and SVG saves of the figure, the left-aligned numbers do lie in columns).
I'd like to see matplotlib have at least the option of aligning using the advance widths of the characters in the horizontal direction and the font-wide ascent and descent (rather than the ascent and descent of the particular glyphs in each text object) in the vertical direction. Is it important to have the option of aligning to the glyph ink, too (and to do it consistently across backends)? As time permits, I'm willing to contribute coding effort.
Craig, I don't know of a work-around at the moment, but I'll write again if I think of one.