Incorrect kerning when drawing text

I’m using “Arial Bold” 20pt SpriteFont:

<?xml version="1.0" encoding="utf-8"?>
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
  <Asset Type="Graphics:FontDescription">
    <FontName>Arial Bold</FontName>
    <Size>20</Size>
    <Spacing>0</Spacing>
    <UseKerning>true</UseKerning>
    <Style>Regular</Style>
    <DefaultCharacter>*</DefaultCharacter>
    <CharacterRegions>
      <CharacterRegion>
        <Start>&#32;</Start>
        <End>&#126;</End>
      </CharacterRegion>
    </CharacterRegions>
  </Asset>
</XnaContent>

And the following code to draw a simple string:

_spriteBatch.Begin();
_spriteBatch.DrawString(SF, "Sample Text Test: tl", new Vector2(100, 100), Color.Black);
_spriteBatch.End();

I have 2 MonoGame projects and they’re giving me very different results on the same .spritefont.

My first project references these packages:

    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
    <PackageReference Include="MonoGame.Extended" Version="3.8.0" />
    <PackageReference Include="MonoGame.Extended.Content.Pipeline" Version="3.8.0" />
    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303">
      <PrivateAssets>All</PrivateAssets>
    </PackageReference>

and gives me this result:
Arial Bold 20pts DesktopGL

My second project references these packages:

    <PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.0.1641" />
    <PackageReference Include="MonoGame.Extended" Version="3.8.0" />
    <PackageReference Include="MonoGame.Extended.Content.Pipeline" Version="3.8.0" />
    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.1.303" />

and gives me this result:
Arial Bold 20pts WindowsDX

Why does the result from my first project look so… bad? It looks like the kerning is incorrect, especially between the ‘t’ and ‘l’ at the end of the string.

If I copy over the .xnb file built from my second project into the first project’s bin\Content folder, then running the first project gives the desired rendering. So I assume it must be something different between whatever’s creating the .xnb files. But I opened my Content.mgcb in both projects and they appear to have the exact same settings for my SpriteFont.

Edit: Ok I’m pretty sure the difference has nothing to do with using MonoGame.Framework.DesktopGL vs MonoGame.Framework.WindowsDX. I downgraded MonoGame.Content.Build.Task from 3.8.1.303 down to 3.8.0.1641 and I’m getting the desired kerning results now. So I guess there’s a bug in the latest version of MonoGame.Content.Build.Task.

The settings I was using on my spritefont in the MonoGame Pipeline Tool was:
Build Action: Build
Importer: Sprite Font Importer - MonoGame
Processor: Sprite Font Description - MonoGame
PremultiplyAlpha: True
TextureFormat: Color (I originally had this set to ‘Compressed’ and was still having the problem)

1 Like

Hi, if you have solved this, please mark your post as the solution so that people know it is solved, and if you can, raise a bug report on the GitHub pages, thanks.

Happy Coding!

It would seem that this change introduced a regression with kerning: Optimized SpriteFont GlyphPacker by TechPizzaDev · Pull Request #7464 · MonoGame/MonoGame · GitHub

I haven’t verified but leaving this here in case anyone wishes to investigate.

3 Likes

Is there a way to workaround this for now without downgrading MG from 3.8.1 (i.e. a way to use content/fonts built with 3.8.0.1641 in 3.8.1.303)? This kerning issue is kind of a big deal for our new title and I don’t have the font knowledge/expertise to fix this in MG’s font processor myself.

I don’t know if I have polluted MGCB installs or something, but I cannot reproduce this kerning bug.
I get the expected good-looking result on both of the given configurations.

Here’s a sample project that demonstrates the issue:

If you run it, it will open 2 windows on top of each other. Drag 1 window out of the way and you should be able to see the differences. The only difference between the 2 projects is 1 references package MonoGame.Content.Builder.Task 3.8.0.1641, and the other references package MonoGame.Content.Builder.Task 3.8.1.303

1 Like

I built one project with 3.8.0.1641 (MGCB and MG in Visual Studio 2019) and one with 3.8.1.303 (MGCB and MG in Visual Studio 2022) and got these results:

3.8.1 vs 3.8.0 arial
Identical SpriteFont file in both environments; referencing Arial at size 50 and built using Color TextureFormat instead of Compressed

To my surprise, when I tried to use the built SpriteFont xnb from 3.8.0 in my 3.8.1 build, it seemed to just work. I had thought in the past I got an error when attempting to do this saying the content was built with the wrong version of MGCB.

2 Likes

I found the culprit:

Commenting this additional code out brings back the old layout. This change was allegedly a fix to some issue, so I feel like this current solution is not correct.
At least it was not my glyph packing PR :slight_smile:

5 Likes

Your glyph packing was fine.
But I stand by my PR. :slightly_smiling_face:

MonoGame does not implement kerning. The spacing between letters in MonoGame’s SpriteFont is based on the horizontal advance in TrueType fonts which is letter spacing.

For MonoGame to implement true kerning, spacing between individual letter pairs needs to be stored in the xnb and rendered in SpriteBatch.DrawString

After updating to 3.8.1 I see incorrect kerning (similar to your example).
I have to use xnb font files generated by 3.8.0 - otherwise text does not look good.

We do this as well because of how odd the spacing is when building fonts in 3.8.1

@nkast I am unfamiliar with the font processing process, but I’d be interested to know why you “stand by” your PR? Is it simply mathematically more correct or some such? I think if it was visually correct, we wouldn’t have so many people noticing/questioning it. Not to mention circumventing it entirely like myself and @Powered_JJ.

1 Like

The change fixes the values that are written in the "kerning’ field of the glyph. The code now produces values that are closer to the letter spacing of the character.
That is what you would get from win32 GetCharABCWidthsA() method for example.

I would say, it made one part of the equation ‘Symantically’ correct.
Mathematically you can often have two wrongs equalling one right :wink:

Fair enough. Not that I get a say, but I would vote given that info, this fix should instead be placed in a comment so it can be applied when the “other end of the equation” is also fixed. Because I understand you’ve fixed one part of it to be technically correct, but as it stands the end product has more noticeably messed up spacing than what we were working with in 3.8.0. Thus, why I and apparently others manage a completely separate MGCB for compiling SpriteFonts.

For the record, I (and presumably others) very much appreciate your efforts to make SpriteFonts better! Just right now this particular update seems sorta half-baked and yet still implemented into stable MG, which is causing some of us some minor headaches, is all.

1 Like

The other issue started manifesting after #7501 was applied.

Hindsight is 20/20, the only thing you can do is to address one issue at the time,
and then move on to the next. If you are going back and forth, you would find your self standing still.

The issue you are seen has to do with the glyph cropping, not the kerning data.
The processor writes 0 while it should write the XOffset (plus FontBitmapLeft which is kinda part of cropping).

1 Like

Whatever the cause, the end result looks bad.
3.8.1 produces text, that just looks wrong - not fit for a polished, final product of a game.

I appreciate the efforts to make MonoGame better. But this change should not make its way into stable branch. MonoGame is a great framework and I hope this issue will get resolved soon.

Does someone know if there’s a way to replace xnb font files when building for Android?

2 Likes

For any platform, I open my .mgcb with a copy of MGCB v3.8.0.1641, set the target platform at the root level, then compile only my SpriteFonts.

1 Like

Seriously. This kerning looks sooo wrong… :smiley:

Przechwytywanie