Correctly computing width of (sub-string in) text generated by Canvas.text()
Hello everyone!
I am having some trouble with correctly computing the width of text strings (in pixels), displayed via Canvas.text().
OpenSesame version
4.0.32
Operating system
Windows 10 / macOS 15.6.1
Backend
PsychoPy / Expyriment / legacy / OSWeb
Expected behavior
This is a follow-up on the issue I originally opened on GitHub. I expected that using a mono-space font would result in
my_canvas.text(line,center=False,x=x_pos,y=y_pos + i * line_spacing)
generating text so that each character is displayed with equal length. However,
my_canvas.text_size(line,center=False)[0]/len(line)
returns different float values for different lines, suggesting that not all characters are of equal width.
Actual behavior
If I understand the response by @sebastiaan correctly, both Canvas.text(line) and Canvas.text_size(line) embed line in a padded box, which affects the displayed text in a way that not every character is of equal width. That would also explain why
w = 0
for c in line:
w += my_canvas.text_size(c,center=False)[0]
w
returns yet another width for line - since the bounding box computed for just c will probably be different from the bounding box computed for the full line?
I don't see a problem with the embedding, since I myself do not notice any character width differences - so they really seem to be minor. My problem is that I need to be able to re-compute the coordinates of all sub-strings later during the analysis: in my experiment I display multiple lines of text (i.e., a paragraph) and track participants' gaze fixations. Later I need to align each fixation with the closest word in the paragraph. My plan was to just compute the word positions as part of the analysis, but now I think I need to do this as part of my canvas preparations. That is, for each set of lines corresponding to a paragraph I would need to do something like this:
coordinates = []
for i, line in enumerate(lines):
my_canvas.text(line,
center=False,
x=x_pos,
y=y_pos + i * line_spacing)
# Store x coordinate for start of each word, width, and y coordinate for later alignment
word_starts, word_widths = calculate_word_coordinates(my_canvas, line, x_pos, font_size)
coordinates.append([word_starts, word_widths, y_pos + i * line_spacing])
my_canvas.show()
var.coordinates = coordinates
# Start tracking, etc.
where calculate_word_coordinates would return the x coordinates corresponding to the first pixel of the first character of each word in line and the widths of each word (also in pixels). However, I do not know how to best define the calculate_word_coordinates function due to the padding inserted by OS, so I would really appreciate some help with that.
Thanks in advance! 😊
Best regards,
Joshua