Skip to content

Align text using advance width, not glyph width #253

Closed

Description

@ddale

Original report at SourceForge, opened Thu May 29 15:12:49 2008

As far as I can tell, the alignment of text strings is based on the bounding box of the composite glyphs--the painted region. In certain cases, this behavior is producing results that I think are undesirable. The attached script illustrates some examples.

In the Y axis labels, the digit one has a narrower glyph and larger right-side bearing than the other digits, so the labels "0.1" and "-0.1" are shifted such that the leading zero and decimal point don't align with the other labels. Because these are tabular rather than proportional figures (numerals), the numerals' advance widths are equal; if the alignment were based on the total advance width of the label, the decimal points and places would align, although there would be more space between the digit one glyph and the axis.

The text objects show a similar vertical misalignment with multi-line strings.

The X axis labels are set with old-style, lowercase, or non-lining figures. The varying extent of the numerals above and below the baseline isn't accounted for by the glyph-based alignment, so the baselines don't line up. Granted, I rarely see old-style figures used in graphs, but they illustrate the behavior well.

I've observed this behavior on 0.91.2 using the WXAgg backend and with PNG, SVG, EPS, and PDF files. It seems to come through the font.get_width_height and font.get_descent calls within RendererAgg.get_text_width_height_descent and, more deeply, from the routines in FT2Font. I haven't gotten the SVN maintenance branch or the trunk running on my machine, so I'm unsure whether the behavior appears with the newer code.

What would be required to align text horizontally using advance widths and vertically using font-wide metrics for ascent, descent, and so on? Thanks.

SourceForge Comments

On Tue Jun 3 17:18:19 2008, jdh2358 wrote:

Logged In: YES
user_id=395152
Originator: NO

Hi Stan -- this is indeed an important feature request, and the trunk is the same as the branch in this regard. I'm not sure what the timeline is to get it done, but it's something I've wanted to add for a while. If you want to take a crack at it.... :-)

JDH

On Thu May 29 15:15:34 2008, stanwest wrote:

Logged In: YES
user_id=2093133
Originator: YES

File Added: textaligntest.png

SourceForge History

  • On Tue Jun 3 17:18:19 2008, by jdh2358: priority: 5
  • On Thu May 29 15:15:34 2008, by stanwest: File Added: 279501: textaligntest.png
  • On Thu May 29 15:12:50 2008, by stanwest: File Added: 279500: textaligntest.py

Activity

  1. pelson commented on Jun 18, 2012

    @pelson
    Member

    @mdboom: I know you've had font dealings lately. Do you think this is this still an issue?

  2. efiring commented on Jul 22, 2013

    @efiring
    Member

    Stan West, the OP, provided the image and test code below:

    textaligntest

    import matplotlib.pyplot as plt
    
    fontdictvert = {'family': 'Georgia', 'size': 18.0}
        # or any font with old-style/lowercase/non-aligning figures (numerals)
    fontdicthorz = {'family': 'Arial', 'size': 18.0}
        # or any font with tabular (not proportional) figures and a narrow
        # glyph for digit one
    
    plt.figure()
    plt.subplot(1, 1, 1)
    plt.xlim(0, 1)
    plt.ylim(-0.301, 0.301)
    plt.xticks(**fontdictvert)
    plt.yticks(**fontdicthorz)
    plt.text(0.2, 0, 'left-aligned\n90123\n01234\n12345\n23456\n34567',
             ha='left', va='center', **fontdicthorz)
    plt.text(0.8, 0, 'right-aligned\n90123\n89012\n78901\n67890\n56789',
             ha='right', va='center', **fontdicthorz)
    
    plt.show()
  3. efiring commented on Jul 22, 2013

    @efiring
    Member

    @pelson, @mdboom, Stan reports that the problem remains: "The behavior in my installed matplotlib, v. 1.2.1, is still as I described in the issue. I can add that there are some differences across backends for the left-aligned multi-line text. With the Agg backend (and the PNG file), the numerals in the "12345" line are left-aligned by their glyphs or ink, so that they're slightly misaligned relative to the columns of the other numerals. In contrast, with the ps, pdf, and svg backends, the "12345" line is aligned with the others. For those backends, perhaps the glyph-based alignment occurs only for non-left alignments."

  4. mdboom commented on Jul 23, 2013

    @mdboom
    Member

    First of all -- sorry this flew under the radar for so long.

    I think it will be easiest to address this as part of my ongoing MEP14 work to overhaul a lot of the text processing. Not because it couldn't be done now, but because we might as well fix these sort of subtle things while fixing a bunch of others, and also to avoid implementing this fix twice. It's sat here for 5 years -- it can sit for a few more months ;)

  5. added this to the milestone on Aug 18, 2014
  6. sgmentzer commented on Jan 22, 2015

    @sgmentzer

    This issue is still present in 1.5.dev1 [edit: under Windows 7 SP1 x64]. I also upgraded FreeType to 2.5.5 with no change.

    Here's some test code. It outputs to PDF the same graph with the six common Windows & Mac TrueType fonts, plus the Abel font (source URL below). SVG exhibits different oddities in the same glyphs.

    The problem is noticeable with Arial and Trebuchet and the '1' glyph, but glaring with Abel and the '4' glyph:

    abel
    Here's the same data aligned in MS Word:

    abel2
    As suggested above, the cause looks to be somewhere in the FT2Font code.

    from matplotlib import rcParams
    from matplotlib.backends.backend_pdf import FigureCanvasPdf as FigureCanvas
    #from matplotlib.backends.backend_svg import FigureCanvasSVG as FigureCanvas
    from matplotlib.figure import Figure
    from matplotlib import lines
    
    data = [ { 'q1': 37.4, 'q2': 4.0, 't': 'Average carton' },
             { 'q1': 31.3, 'q2': 0.2, 't': 'Special order'},
             { 'q1': 7.2, 'q2': 1.1, 't': 'Standard price'},
             { 'q1': 47.1, 'q2': 0.4, 't': 'Double final limit'},
             { 'q1': 1.1, 'q2': 1.4, 't': 'Lorem ipsum dolor sit amet'}
            ]
    
    col1 = 0.3
    col2 = 0.375
    col3 = 0.4
    top = 0.8
    spacing = 0.04
    fonts = [ 'Arial', 'Times New Roman', 'Georgia', 'Tahoma', 'Trebuchet MS', 'Verdana', 'Abel' ] # Abel source: http://www.google.com/fonts/specimen/Abel
    rcParams["font.size"] = 12
    
    for i in range(len(fonts)):
        rcParams["font.family"] = fonts[i]
    
        fig = Figure()
        canvas = FigureCanvas(fig)
    
        ax = fig.add_subplot(111)
        ax.set_yticks([x / 10.0 for x in range(0, 11)])
        ax.set_xticks(ax.get_yticks())
    
        ax.text(0.5, 0.9, fonts[i], horizontalalignment='center')
        for j in range(len(data)):
            r = data[j]
            ax.text(col1, top-j*spacing, r['q1'], horizontalalignment='right')
            ax.text(col2, top-j*spacing, r['q2'], horizontalalignment='right')
            ax.text(col3, top-j*spacing, r['t'])
    
        ax.add_line(lines.Line2D((col1, col1), (0.85, 0.60), linestyle='-', linewidth=0.1, color='k'))
        ax.add_line(lines.Line2D((col2, col2), (0.85, 0.60), linestyle='-', linewidth=0.1, color='k'))
        ax.add_line(lines.Line2D((col3, col3), (0.85, 0.60), linestyle='-', linewidth=0.1, color='k'))
    
        fig.savefig('mpl-' + fonts[i] + '.pdf')
    
  7. self-assigned this
    on Jan 23, 2015
  8. added a commit that references this issue on Jan 23, 2015
    abd9b8a
  9. added a commit that references this issue on Jan 27, 2015
    c6d0183
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions