LSYS/forestplot

Known Issue: Left-flushing of annotations relies on the monospace font

sjiang40 opened this issue · 3 comments

I would like to use different fonts (e.g. Helvetica, Arial) to match journal font requirements for publication-ready figures.

A potential fix could be to use matplotlib.pyplot.table to render the table text to allow left-align with any font.

LSYS commented

Hey sjiang40, thanks so much for pointing me to matplotlib.pyplot.table. I was not aware of this. Seems like a potential solution to aligning the y-labels. Will investigate this for a future release. @sjiang40 Let me know if it's something you want to work on.

@LSYS I would love to help but I will be on rotations in the hospital for the foreseeable future and I will have to spend my free time working on other research. Assuming you get to this before I get a chance to, I suspect matplotlib.pyplot.table will be a viable solution for this.

Alternatively, if matplotlib.pyplot.table ends up being too much of a code refactor, I recently wrote some code to add multiple Text() objects in series as if it were a single object. The purpose was to add a bolded subfigure label with normal subfigure title without using LaTeX (e.g. "A) Subfigure title"). I think the core algorithm can be adapted to align columns in a table independent of the font. The steps would essentially be:

  1. Place all text on the axis
  2. Draw the plot
  3. Identify the maximum width Text() object in each column
  4. Horizontally shift each column accordingly

Here's some example code from my use case (note: this example is not robust and assumes va= 'center' and loc='center'):

import matplotlib.pyplot as plt

# Assign variables
fig, ax = plt.subplots(1, 1)
renderer = fig.canvas.get_renderer()
transform = ax.transAxes

# Draw center line reference
ax.vlines(.5, ymin=0, ymax=1, transform=transform)

# Place title and label on ax in arbitrary location and draw objects
title_obj = ax.set_title('Subfigure title', va='center', loc='center')
label_obj = ax.text(.5, .5, 'A) ', va='center', fontweight='bold', transform=transform)
plt.draw()

# Get title coordinates, width, and position
bb = title_obj.get_window_extent(renderer=renderer)
bb_coords = bb.transformed(transform.inverted()).get_points()
title_x1, title_x2, title_y1, title_y2 = bb_coords[0][0], bb_coords[1][0], bb_coords[0][1], bb_coords[1][1]
title_w = title_x2 - title_x1
x_pos = title_obj.get_position()[0]
y_pos = (title_y1 + title_y2) / 2

# Get label coordinates and width
bb = label_obj.get_window_extent(renderer=renderer)
bb_coords = bb.transformed(transform.inverted()).get_points()
label_x1, label_x2, label_y1, label_y2 = bb_coords[0][0], bb_coords[1][0], bb_coords[0][1], bb_coords[1][1]
label_w = label_x2 - label_x1

# Define shift offsets
title_offset = label_w / 2
label_offset = -(title_w + label_w) / 2

# Shift title and label horizontally
title_obj.set_position((x_pos + title_offset, y_pos))
label_obj.set_position((x_pos + label_offset, y_pos))

fig.set_facecolor('w')

This code creates this image:
image

would be good to see the text to allow left-align with any font.