Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Bug]: Text rotation leads to characters being misplaced within their bounding boxes. Attempted solution provided. #23021

Copy link
Copy link
Open
@paulcbogdan

Description

@paulcbogdan
Issue body actions

Bug summary

plt.text(...) is not rotating text correctly. This becomes clear when looking at their bounding boxes, where you can see that a character's position within the bounding box differs depending on the rotation (and sometimes, the text will even exit the box).

Code for reproduction

import matplotlib.pyplot as plt

def plot_rotation_period(y, rotation, r):
    c0 = plt.gca().annotate('.', xy=(0.5, y), xytext=(0.5, y), rotation=rotation, fontsize=80,
                            rotation_mode='anchor', fontfamily='monospace', va='bottom', ha='center',
                            transform_rotates_text = False)

    bb0 = c0.get_window_extent(renderer=r).transformed(plt.gca().transData.inverted())
    rect0 = plt.Rectangle((bb0.x0, bb0.y0), bb0.width, bb0.height,
                         facecolor="C1", alpha=0.3, zorder=2)
    plt.gca().add_patch(rect0)

if __name__ == '__main__':
    fig = plt.gcf()
    r = fig.canvas.get_renderer()
    fig.set_size_inches(15, 15)

    plt.xlim(0, 1)
    plt.ylim(0, 1)

    plot_rotation_period(0.8, 0, r)
    print('1')
    plot_rotation_period(0.6, 90, r)
    print('2')
    plot_rotation_period(0.4, 180, r)
    print('3')
    plot_rotation_period(0.2, 270, r)

    plt.plot([0.5, 0.5], [0, 1], linestyle='--')
    plt.show()

# plt.savefig('example_C.png') # this issue becomes a bit clearer if you do plt.savefig()

Actual outcome

These examples were created from the code above, which rotates the text 0/90/180/270 degrees and plots its bonding box. When the character is a ".", it looks like this: https://imgur.com/dBgTRBA

The "." makes the issue obvious, although this issue appears for all characters, e.g., "C": https://imgur.com/a/u3UamAb

Changing the horizontal/vertical alignment does not fix this. I tried every possible combination, and although it moves the text around, it never does so never in the correct way.

Expected outcome

Correct outcome for "C": https://imgur.com/2XT7wdA
Correct outcome for ".": https://imgur.com/Zx22cV1

Here, we can see that the character is in the same position relative to its bounding box for each possible rotation.

Additional information

I took a stab at trying to fix this. It seems like the issue is related to the Descent and/or Offset/Bearing of the font.

I took a look at backend_agg.py, where the text is being drawn. My solution may is quite crude and violates Chesterton's fence. However, I found that if I comment out some lines in backend_agg.RendererAgg.draw_text(...), this fixes the issue (highlighted here):

    def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
        # docstring inherited

        if ismath:
            return self.draw_mathtext(gc, x, y, s, prop, angle)

        flags = get_hinting_flag()
        font = self._get_agg_font(prop)
        if font is None:
            return None
        # We pass '0' for angle here, since it will be rotated (in raster
        # space) in the following call to draw_text_image).

        font.set_text(s, 0, flags=flags)
        font.draw_glyphs_to_bitmap(
            antialiased=mpl.rcParams['text.antialiased'])

        d = font.get_descent() / 64.0
        #The descent needs to be adjusted for the angle. [original comment from the package]
        # xo, yo = font.get_bitmap_offset() [I deleted these next lines, commented out here]
        # xo /= 64.0 
        # yo /= 64.0 
        # xd = d * sin(radians(angle)) 
        # yd = d * cos(radians(angle))
        # x = round(x + xo + xd)
        # y = round(y + yo + yd)
        self._renderer.draw_text_image(font, x, y + 1, angle, gc)

By commenting out those lines, I was able to generate those expected outcome pictures above. I tried this for serif fonts and different vertical/horizontal alignments, and it seems to work as expected. Should I do a PR (or would further tests be needed, if so which)?

I'm having trouble understanding why this code would be here in the first place. Maybe there was some code changed upstream/downstream which already addresses the offset/descent issue, and so the code I pointed out is doing it again, leading to problems?

I think someone before tried to come up with a solution for a related problem (see issue), although their fix was not accepted.

Operating system

Windows 10

Matplotlib Version

3.4.3

Matplotlib Backend

Qt5Agg

Python version

3.8.12

Jupyter version

No response

Installation

conda

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.