UI/NameFit.cs

Utility for computing how many pixels a string will occupy in Inter Regular and selecting the largest whole-pixel font size that fits a fixed width. It uses a precomputed advance table for ASCII 32-126 and heuristics for other Unicode ranges to estimate width without layout.

Obfuscated Code
using System;

namespace NZombies;

/// <summary>
/// A player's name at the biggest size that fits its row (the user, 2026-10-05: *"if the name is too long, the font size
/// decreases so it always fits"*). The lobby's player list asks it for every name. Anything else that draws a name in a fixed
/// width can ask it too.
///
/// ⚠️ FROM INTER'S OWN WIDTHS: NOT A GUESS, AND NOT A MEASUREMENT AFTER LAYOUT. A panel can't say how wide its text is until it
/// has been laid out, and a size chosen afterwards would draw every long name too big for a frame first. The table is Inter
/// Regular's advance for ASCII 32–126, in thousandths of an em, read from s&amp;box's `core/fonts/Inter-Regular.ttf`. Summed,
/// it matched the font's own measure to within 0.3 px on a 32-letter name at 16 px. Anything outside ASCII counts as a full
/// em (CJK, kana, hangul, emoji) or two thirds of one (accented Latin, Cyrillic, Greek…), which errs small rather than
/// spilling.
/// </summary>
public static class NameFit
{
	static readonly short[] Inter =
	{
		281, 278, 403, 631, 638, 813, 639, 221, 363, 363, 500, 659, 280, 460, 276, 357, 626, 465, 605,
		636, 643, 607, 624, 571, 616, 624, 276, 280, 659, 660, 659, 508, 936, 676, 651, 727, 719, 598,
		587, 744, 740, 264, 542, 652, 562, 889, 753, 762, 635, 762, 639, 638, 641, 741, 676, 949, 642,
		664, 625, 362, 357, 363, 470, 453, 497, 564, 621, 558, 621, 582, 361, 610, 592, 237, 237, 544,
		238, 870, 586, 596, 609, 610, 372, 524, 364, 582, 557, 813, 540, 557, 542, 362, 327, 362, 660,
	};

	/// <summary>How wide this text is in Inter Regular at this size, in px.</summary>
	public static float Width( string text, float size )
	{
		if ( string.IsNullOrEmpty( text ) ) return 0f;

		var em = 0f;
		foreach ( var ch in text )
		{
			if ( ch >= 32 && ch <= 126 ) em += Inter[ch - 32];
			else if ( char.IsLowSurrogate( ch ) ) continue;                  // the pair's width went on its first half
			else if ( char.IsHighSurrogate( ch ) || ch >= 0x2E80 ) em += 1000; // emoji, CJK, kana, hangul: a full em
			else em += 667;                                                   // accented Latin, Cyrillic, Greek…: two thirds
		}

		return em * size / 1000f;
	}

	/// <summary>
	/// The biggest whole px size, from <paramref name="max"/> down to <paramref name="min"/>, at which this text fits in
	/// <paramref name="room"/> px.
	///
	/// ⚠️ 3% IN HAND for the renderer's hinting, which the table can't see. ⚠️ THE FLOOR IS THERE FOR A NAME NO SIZE COULD SAVE:
	/// at 8 px even 32 W's, Steam's longest name in its widest letter, take 243 px, inside the lobby's 269.
	/// </summary>
	public static int Size( string text, float room, int max = 16, int min = 8 )
	{
		var at = Width( text, max ) * 1.03f;
		if ( at <= room ) return max;

		return Math.Clamp( (int)MathF.Floor( max * room / at ), min, max );
	}
}