You are hitting a special case in the heuristic for the bounding box calculation, because your âtext stringâ only consists of a single character.
The culprit is likely line 313 of DrawFormattedText.m:
blankbounds = Screen('TextBounds', win, 'X', [], [], 1, righttoleft);
Here, as a heuristic, the bounding box blankbounds
of the character capital âXâ is used as a prototype for the baseline height of characters, ie. the height of characters which do not have descenders, e.g., xXwWoO etc. but not ygjpqQ. See line 324:
baselineHeight = RectHeight(blankbounds);
baselineHeight is by how much the bounding box must be shifted vertically from computed text bounds. If baselineHeight is wrong for a given text string then the bounding box is shifted. If your text string doesnât have at least one character that is as tall as a X, the bounding box will be shifted by the difference of the tallest character and X. Therefore the error is small or negligible for typical text strings, but not for strings with only minor characters, or worst case single characters, like in your case. You can see that âsmallâ characters in your example have a large error, but not âtallâ ones. You also see that DrawFormattedTextDemo produces less inaccurate bounding boxes for the demo text.
Now the way to get a near-perfect bounding box would be to not use the returned bounding box textbounds
, but instead the 4th return arg wordbounds
in your case, or for longer text to compute the bounding box over all wordbounds bounding boxes of all words in a text string. That gives as perfect results as the chosen text font allows, iow. any mistake is caused by mistakes the font designers made when creating the font definition file - storing wrong or inaccurate geometry information in the glyph definitions. We could do that internally. The problem with this is that computing per word bounding boxes and then the bounding box over all bounding boxes is much slower than computing one overall bounding box via the chosen heuristic.
And a perfect bounding box can be only computed via image processing, see help TextBounds
for that. Of course that is another 1-2 orders of magnitude slower.
Text rendering is a slow operation in itself, the text formatting done by DrawFormattedText() and the more flexible and higher quality DrawFormattedText2() is even much slower. Therefore we always have a speed vs. accuracy vs. quality tradeoff. For a normal typesetting application, a word processor, or a text editor, accuracy is way more important than speed, as long as the human in front of the keyboard canât type faster than the software can draw. For Psychtoolbox that may have to draw some text and stimuli at > 200 fps, speed is of great importance, so the bounding box heuristic is such a tradeoff. Hence the fuzzy language in the help text that it can be of limited accuracy or an approximation. You are hitting the edge case of really bad here.
Ofc. line 313 could be improved with something more clever for such cases with better heuristics. But one has to be careful not to make things too slow. And especially careful to not make it incompatible/broken for Octave or older Matlab versions which have various limitations wrt. Unicode handling, and operating system differences, one has to work around - see the various casting operations between char() and double() and weird contortions to deal with that. Or with non-western non-alphanumeric character / glyps sets, e.g., chinese, japanese, hebrew, special symbols like emojies etc. After all, unicode has ten-thousands of code points, and western characters only make up < 255 of them.
I think DrawFormattedText2 can handle some of these cases better, but is slower, and I also may misremember.