- Home /
Getting width of a character in new 4.6 UI system?
I'm trying to measure word widths in Unitty 4.6's new UI system, by adding up the character widths.
Unfortunately, the following always generates a character width of zero. I'm guessing it doesn't apply to the new UI system?
var ci : CharacterInfo;
dotFont.GetCharacterInfo( "T"[0], ci, 32 ); //Font size 32
Debug.Log (ci.width);
What I want to obtain is the rendered width of a capital letter "T" (or any char) at 32-point size. (dotFont is assigned in the Inspector, and renders fine visually in Text objects and InputFields.)
Is there a way to do that?
Thanks for any ideas! (My last resort will be to measure every character in Photoshop and hard-code the values. Or come up with a way to automate the task using a Content Fitter, but that's a whole other rabbit hole...) If it helps, I'll eventually move to Unity 5.
If it can't be done, at least here's a hack that has provided me with a decent width estimate, in case it helps anyone else. (We'll see how it is in the long term, but initial testing has gone fine without the need to know precise pixel widths.)
This code generates a word length in "number of characters," but weighted (approximately) for the variation in character widths. The numbers in the five size "buckets" are based on my measurements of DIN Alternate Bold, so they will vary with other fonts. (I picked a representative character from each bucket--generally the largest common character--and measured it in Photoshop against my "default" sized character: a lowercase "h." I used a row of 50 characters to get a more precise comparison.)
So, as an example (with made-up numbers), the word "deep" might have a width of 4, but "DEEP" might be 4.8, and "lint" might be only 3.2. All have four characters, but compared to a hypothetical monospaced font, the rendered widths vary.
Once you have that "weighted number of characters," you can multiply it by a constant of your choice, or whatever gives you the most useful estimate of the width in pixels. (And you may be measuring the width of an entire string, not just one word at a time as I've done here—I only wanted to know the longest word, so I could force my own custom wrapping/fitting that works better than Unity's built-in way. Unity will sometimes wrap mid-word, which I did not want: I wanted to shrink to fit ins$$anonymous$$d, while still having wrapping between words. Seems to work so far!)
(Won't let me put code in this comment; see code in next comment.)
var myString : String;
var narrowSize : float = .536; //Group characters into size "buckets" in lieu of precisely measuring each
var defaultSize : float = 1.0;
var mediumWideSize : float = 1.247;
var wideSize : float = 1.605;
var extraWideSize : float = 2.0;
var words : String[] = myString.Split(" \n".ToCharArray()); //Split by spaces or newlines
var longestWord : float = 0;
for (var w = 0; w < words.Length; w++ ) { //Calculate width of each word
var wordWidth : float = 0;
for(var c=0; c < words[w].Length; c++) { //Estimated character widths in categories:
var unicode : int = System.Convert.ToInt32(words[w][c]);
var ch : String = ""+words[w][c]; //Char to string
if ( "—…Æ".Contains(ch) ) {
wordWidth += extraWideSize;
} else if ( "wmW$$anonymous$$@%€œŒæ¼½¾√".Contains(ch) ) {
wordWidth += wideSize;
} else if ( "tfjl|!¡.,:;'‘’`()[]{}iîïíīįìiIÎÏÍĪĮÌł".Contains(ch) ) {
wordWidth += narrowSize;
} else if ( "&<>ÈÉÊËĒĖĘŸÛÜÙÚŪÔÖÒÓŒØŌÕÀÁÂÄÆÃÅĀŚŠŽŹŻÇĆČߣÑŃ".Contains(ch) || (unicode>64 && unicode<91) ) { //65–90 are capitals
wordWidth += mediumWideSize;
} else if ( "–“”".Contains(ch) ) {
wordWidth += defaultSize;
} else if ( unicode > 567 ) { //$$anonymous$$any extra-wide non-Latin characters above 567 (including Asian characters)
wordWidth += extraWideSize;
} else {
wordWidth += defaultSize;
}
}
longestWord = $$anonymous$$athf.$$anonymous$$ax(longestWord, wordWidth);
}
Debug.Log(longestWord);