Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Supported by

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

Sign In or Register to comment.