Added getAscent and getDescent functions to Font #3053
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR adds functions to the Font class to retrieve the ascent and descent of the font.
I marked the PR as a draft because there are some implementation details that still need to be discussed.
Motive
SFML doesn't really provide good way to vertically position text. These functions will help with that.
If two text objects using the same font and character size are given the same Y position, then their baselines do end up at the same vertical position as intended (irrelevant of which characters each text contains). We also know the location of the baseline, as the Text class places it at y = character size.
There are however two issues:
We don't really know the full line height. If I render the letter
x
, it's bottom location will be atm_characterSize
, and it's top position will be some offset that can be found withtext.getLocalBounds().getPosition().y
. However if I render the letterg
, it extends below the baseline. I need to know the maximum amount that any text is going to go below the baseline to e.g. position text inside an edit box without the text being placed too low and be rendered outside the rectangle. Just knowing the position of the baseline isn't enough, I also need the font's descent, which is what the newgetDescent()
function will provide.You could estimate the descent by subtracting the character size from the line spacing, but this could be off by a few pixels (and completely wrong for some broken fonts). The alternative that I used was to just get the information from the
g
glyph, but that only works if the font contains that character, and I learned today that_
is sometimes placed even lower than ag
.If you render text with Y position at 0 then there is a gap at the top. This is normal because you need to keep space for larger characters, it's normal for
x
to have a larger gap thanB
at the top. The problem is that the gap is too large. When using DejaVuSans with font size 16, the highest possible characters (e.g.Ê
) are only 15 pixels tall). No matter what string you render, the top pixel will always be empty. Because the empty pixel (or more pixels for larger font sizes) is considered part of your line height, trying to vertically center text will always cause the text to be visually rendered too low. This is where the newgetAscent()
function comes in, it will return the real maximum character height (15 in this example) so that we don't have to wrongly assume that it equals the character size.Discussion question 1: should sf::Text render the text higher? Instead of placing the baseline at
getCharacterSize()
, should it be located atgetAscent()
so that there is no empty space above the "Ê" character?Example
In the following example a text is drawn with character size 160 (an intentionally large number to make the issues more visible).
A yellow background is rendered to show where the left, top, right and bottom of the text object are located.
As you can see in the image, the baseline is located at top 160 (= character size). The tallest character however is only 149 pixels, the top 11 pixels are always blank no matter which string you use. Without the
getDescent()
function, it would also be hard to figure out that the text can extend at most 38 pixels below the baseline and thus 198 pixels from the Y position of the text.With both the ascent and descent we know the maximum space occupied by any string (149 + 38 = 187), which can be used to better position the text vertically.
Here is the example code:
The code has the following output:
Discussion question 2: should the values returned by getAscent and getDescent be rounded up to match what will be drawn instead of being the exact number returned by freetype?
Discussion question 3: FreeType and SDL_ttf return the descent as a negative value. The getDescent function currently returns a positive number. Which behavior should we use?
Note on testing: I did not test theif (!FT_IS_SCALABLE(face))
branches, I don't know what kind of font I would need for that.Edit: I managed to test the non-scalable branch with a bitmap font from https://github.com/olikraus/u8g2/tree/master/tools/font/bdf