New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added a method to set and get additional spacing between characters. #928
Conversation
include/SFML/Graphics/Text.hpp
Outdated
@@ -145,6 +145,21 @@ class SFML_GRAPHICS_API Text : public Drawable, public Transformable | |||
void setCharacterSize(unsigned int size); | |||
|
|||
//////////////////////////////////////////////////////////// | |||
/// \brief Set the addition character spacing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"additional"?
Except one doc string, this looks good to me. 👍 🐈 |
I'm not 100% sure. How about introducing scaling of the horizontal (and vertical) offsets rather than an absolute offset? Or in other words, make character spacing and line spacing configurable. As far as I know, stuff like s p a c e d o u t t e x t would usually scale with the actual font/glyph's default offsets. |
Thanks, I updated the doc string! I also already thought about line spacing. Although I personally didn't need it yet, it has been requested a couple times on the forum, for example here. Laurent more or less agreed to it here. I also think it is a basic typography tool and it would be a useful addition to About the scaling factor vs. absolut offset: I took a quick look around and here is what others do. GIMP uses only absolut offsets for both letter and line spacing. In LibreOffice letter spacing can only be set using absolut offsets. Line spacing can be defined by both offsets and factors (double line, 1.5 line etc.). In CSS the Another small thing: In this thread the name |
Letter spacing might be the traditional term, but modern typefaces have far more characters than just letters (like dingbats). So I'd stick to SFML's character. Scaling vs. offsets: If offsets are more common, stick with these. :) |
Doesn't this implementation add one-too-many |
I don't think this is relevant. If there is a common term (which is the case), we should use it, not work our way around conventions. Interesting anecdote: in German, a letter has a typesetting origin, it's not simply an alphabetic character. |
Okay, if it's "official". :) |
👍 for both absolute offsets and line spacing. What do others say? |
@mantognini: I don't think so. If I understand the implementation correct, the bounds are computed before the advance is added. So for the last letter in a non-empty string the width is computed, then the advance is added, but then the loop is left and the bounds stay correct. OK, so I guess we'll go with |
@Foaly could you make sure that's the case? E.g. By checking that a one char text has always the same size regardless of the spacing. Just to be sure :) |
@mantognini: I did a quick check. A text with one character returns exactly the same bound compiled with this branch and with current master. Also I updated the commit. Changed the names and the documentation accordingly. |
Alright, so I implemented line spacing. The implementation follows the letter spacing one closely. It's simply an offset, that is added onto the line spacing from the font. |
Then do it in a separate PR please, don't mix unrelated changes. |
Look at my text outline PR, I already refactored some stuff quite a bit. |
Thanks! Yeah I had something similar in mind. A while back I submitted #442 which already incorporated some of those changes, but the refactoring was never added, because vtab got removed and the PR was closed. I'll probably have time to submit the changes tonight. @LaurentGomila I know making a separate pull request is good practice and I can do it, if you want. But are you sure in this case? I mean the changes are quiet small and related in my opinion (added new functionality -> refactored existing method). Also a separate pull request would mean merge conflicts. Your call though. |
@Foaly As a compromise, and to avoid unnecessary work with merge conflicts, you could add to this PR a separate commit that contains only the refactoring. |
Alright here are my changes! I'd love to hear some feedback. |
As I already refactored the code to add a line to an external method in #840 I'm not sure why you did it again (and the method will be different from text outline version since two sets of vertices are required for text outlining). It is just going to cause merge conflicts and doing duplicate work is kinda a waste of time. |
If you take a closer look, you'll see that I the method has a different sigature and a slightly different implementation. I found it more expressiv this way. |
But @zsbzsb has got a point: now we have definitive merge conflicts and the merge order determines who is "lucky" and needn't fix his code. Since zsbzsb has opened his pull request already in March, I suggest we use his code... and if needed, you @Foaly adapt it correspondingly for this PR (or already give feedback now). I don't think it makes a lot of sense to rewrite everything now. We should wait until his PR is merged, so that you can rebase on master. |
I opened a pull request with that had this exact refactoring almost two years ago... But anyway, if you want I can take that change out and give @zsbzsb some feedback on how to improve his code. |
src/SFML/Graphics/Text.cpp
Outdated
@@ -317,8 +317,8 @@ Vector2f Text::findCharacterPos(std::size_t index) const | |||
|
|||
// Precompute the variables needed by the algorithm | |||
bool isBold = m_style & Bold; | |||
float whitespaceWidth = m_font->getGlyph(L' ', m_characterSize, isBold).advance + m_letterSpacing; | |||
float lineSpacing = m_font->getLineSpacing(m_characterSize)+ m_lineSpacing; | |||
float whitespaceWidth = m_font->getGlyph(L' ', m_characterSize, isBold).advance * m_letterSpacingFactor; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
float whitespaceWidth = m_font->getGlyph(L' ', m_characterSize, isBold).advance * m_letterSpacingFactor;
float whitespaceWidth = m_font->getGlyph(L' ', m_characterSize, isBold).advance;
float letterSpacing = ( whitespaceWidth / 3.0 ) * ( m_letterSpacingFactor - 1.0 );
whiteSpaceWidth += letterSpacing;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearance between characters is usualy 1/3 of the space width, so:
for m_letterSpacingFactor = 0.0 the spacing will be -1/3 of a whitespaceWidth (equiv. to -100 in Photoshop )
for m_letterSpacingFactor = 1.0 the spacing will be 1/3 of a whitespaceWidth (equiv. to 0 in Photoshop )
for m_letterSpacingFactor = 2.0 the spacing will be 1/3 of a whitespaceWidth (equiv. to +100 in Photoshop )
for m_letterSpacingFactor = 3.0 the spacing will be 2/3 of a whitespaceWidth (equiv. to +200 in Photoshop )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Foaly Please let me know if you could compile those changes to test my theory.
This is a big problem. Indeed, we should apply the spacing factor to the empty space between characters and not to the global advance. I'm not sure if those arbitrary calculations (whitespace_width / 3) are "correct"; I even wonder if for spacingFactor == 1.0 it produces the exact same result as before this PR. Can't we use some extra Glyph information? For example, subtracting the glyph's width from its advance should in theory give the empty space. And we shouldn't forget about kerning, too. |
I seriously doubt we can extract the type width from the font as I looked at some fonts in the editor and the width already contains the clearance between the characters. My math is the closest approximation you can get. For the large font like 60pt the whitespace = 25 pixels, the clearance between the chracters = 9 pixels: then you add it instead of multiplying later: If the getLineSpacing function returns float we should be fine, but if it's an int we are screwed for small characters. That's why I would need this compiled to make sure I'm right. |
Thank you for testing and reporting! Indeed there seems to be an error in the implementation. |
I've rendered some fonts in Photoshp in large scale and counted pixels and checked how the clearance is behaving for different letter spacings. it was almost always 1/3 of a space. I did not count the offset between the characters as this changes from character to character, but rather the delta between different letter spacing. Delta was the same for each glyph pairs. For Arial additional clearance is whiteSpace / 2.77(7) * ( factor -1.0 ) |
If .advance returns the distance with kerning applied that's even better as according to my tests, the delta in pixels is the same for each letter pair when you set +100 or +200 tracking in Photoshop |
I don't know what editor you're talking about, but in SFML,
It doesn't, and it can't since kerning depends not only on the character but also on the next one (in other words, it is defined for each possible pair of characters). Kerning is retrieved with a dedicated function from sf::Font. |
No it does not. Again, it's defined as the width of the glyph's bitmap, so there's precisely no extra space in it. It's not taken from the font's metrics, just deduced from the bitmap. However I just tested my idea, and it doesn't work. The "extra space" is not what we want to use, as some glyphs will have a big one and some others none at all, leading to inconsistent results within the same text. Moreover, it doesn't correctly applies to the space character, which has only extra space. And regardless of the chosen solution, I'm not sure what to do with the kerning: it's a negative offset, which means that it brings glyphs closer to each other. Applying the factor to it would make the glyphs even more close, not sure this is what we want. |
Could you try my method just to be sure. If it's close enough I'm fine with it as long as it does not produce uneven spacings between characters. |
I'll try to find how other libraries implement it, and I'll give your solution a try if I get some time to test it. |
Thanks |
Qt have both options, and the relative spacing is implemented exactly as in this PR: they multiply the whole advance by the factor; which leads to the same weird results. This is really disappointing. Others seem to use absolute offsets rather than relative factors -- maybe this is the reason 😞 |
I've built SFML with my changes and the result is better than I anticipated. |
This is indeed really nice :) You should also test interaction with kerning (add "AV" to your text, for example), and tiny character sizes (below 10). |
After thinking more about it, I think it is a good idea to base the spacing on something that is specific to the font and character size, but remains the same for each glyph. It's not accurate, because strictly speaking, applying a factor should modify the existing spacing and not some artificial one, but as we've seen, this is what produces the best looking results. In the end, it's like a fixed offset, but which depends on the character size (I guess this is what we'd get with As an additional test, you can try with a monospace font, just to make sure it still looks good with those fonts. If your modification successfully passes all these tests, then I'm ok for applying it. But I'd like to get feedback from other members (including @Foaly and other people involved in this PR), if possible. |
The spacing of small fonts and AV pairs looks also fine, except one thing. Smal fonts are a bit blurry when I adjust the scaling factor, but that’s ro be expected as the position is no longer snapped to pixels and small fonts position is optimizd to look crisp. I’m experimenting now to see if it can be overcome by quantizing the offsets. |
Oh the irony! Excuse me but I think this is hilarious! The second, simpler solution is to simply add an offset, that is added between two characters. For historic reasons I am gonna favour that one. |
I do not see any irony here. Why would you prefer a fixed offset that is not dependant on the font size? |
It is. I tested it, and it looks terrible. Extra spacing between characters must not depend on the actual one, but rather on some fixed value which only depends on character size.
I personally like @oomek's solution. It depends on the character size so it is indeed a factor, however it remains consistent within the whole text (doesn't depend on individual characters). I would like to know your specific feedback/comments about this solution 😃 |
Well it is ironic, because we spend about two years discussing whether to use factors or offsets. And now it turns out factors have this major flaw. But it was more of a design discussion and appearently this was overlooked. So I guess I also have to appologize for not testing thoroughly enough. |
SegoeUI Forgive me for my lack of professionalism, but I did not fork SFML, I didn't want to add a duplicate of your commit with just some minor changes. would it be ok to just upload the text.cpp alone and post here the link? |
Sure! Or maybe just the diff. |
Not sure still If I have to ammend the lineSpacing as well. Will look into it when my Fibromyalgia flare goes away. |
Line spacing should be ok. |
Looks fine by me. I'd say you could send a pull request for it. |
SFML Tasks
This is my code, that I promised in this forum thread.
It is pretty straight forward. Here is some example code to test with:
The font Bilbo Swash Caps Regular is taken from here.