Length of a string in pixels
Asked Answered
B

4

8

I'm populating a dropDownList with arrayCollection of strings. I want the width of the drop down list control to match with the size (in pixels) of the longest string in the array collection. The problem I'm facing is: the font width of the strings in the collection are different e.g. 'W' looks wider than 'l'. So I estimated the width of a character to be 8 pixels but that's not pretty neat. If a string that has many 'W' and 'M' is encountered the estimation is wrong. So I want precise pixel width of strings. How can i get the exact length of a string in pixels??

My solution that estimates all character to be 8 pixels wide is given below:

public function populateDropDownList():void{
    var array:Array = new Array("o","two","three four five six seven eight wwww");
    var sampleArrayCollection:ArrayCollection = new ArrayCollection(array);
    var customDropDownList:DropDownList = new DropDownList();
    customDropDownList.dataProvider=sampleArrayCollection;
    customDropDownList.prompt="Select ...";
    customDropDownList.labelField="Answer Options:";
    //calculating the max width
    var componentWidth=10; //default
    for each(var answerText in array){
     Alert.show("Txt size: "+ answerText.length + " pixels: " + answerText.length*9); 
     if(answerText.length * 8 > componentWidth){
      componentWidth=answerText.length * 8;
     }
    }
    customDropDownList.width=componentWidth;
    answers.addChild(customDropDownList);
   }

Any idea or solution is highly valued.

Thanks

Beeves answered 7/1, 2011 at 17:27 Comment(0)
B
7

To get a more accurate measurement, you can populate a TextField with the string, then measure the width of that TextField's text.

Code:

function measureString(str:String, format:TextFormat):Rectangle {
    var textField:TextField = new TextField();
    textField.defaultTextFormat = format;
    textField.text = str;

    return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
}

Usage:

var format:TextFormat = new TextFormat();
format.font = "Times New Roman";
format.size = 16;

var strings:Array = [ "a", "giraffe", "foo", "!" ];

var calculatedWidth:Number = 50;    // Set this to minimum width to start with

for each (var str:String in strings) {
    var stringWidth:Number = measureString(str, format).width;
    if (stringWidth > calculatedWidth) {
        calculatedWidth = stringWidth;
    }
}

trace(calculatedWidth);
Bushcraft answered 7/1, 2011 at 17:49 Comment(12)
@Beeves Believe you need to add a validateNow() call after assignign the string but before checking the textWidth/textHeight the assignment of the string causes invalidation of the size but the re-calculation of the size isn't synchronous so you have to force the validationMusil
Great! I liked the solution; i've a little problem though. It's not getting the right pixel width. I gave it this string in the array. var strings:Array = [ "a", "giraffe", "foo", "The ArrayCollection object does not represent any" ]; Now it got the textwidth to be 245 but in fact the size was 276. So a scroll bar shows on my drop down list. From your code I removed the part that has textFormat, so i made the measureStr function a 1 argument functionBeeves
@Rose: The TextFormat affects the font (-face and -family) that is used when measuring the text -- it should be changed to match that of the drop-down list exactly (though I'm not sure what that is). After removing the textFormat stuff, does it give the right length? If so, I suspect it's because the default text format is the same as what's being used in the drop-down list.Bushcraft
OK, I ran some tests: At size 12, Times New Roman, using your sample strings, I got your width of 245. Using the same strings but with a format with font-size 14, I get 284. I'm not sure which font the drop-down list is using, but there may also be some margins to account for.Bushcraft
@Cameron, right... the font should match... I got your point. I was thinking that flex is going to use the same font if I don't explicitly tell it to use a font. I thought it was going to use the default font. That's why I removed the format parts. I'll try formatting the textField and dropDownList and write back in a moment. Thanks.Beeves
@Rose: I tried it without the FontFormat as well, and it yields the same width as Times New Roman size 12 -- so I'm presuming this is the default for TextFields (but as you say, the dropDownList might be different). In any case, this will only get you the size of the text (no margins) -- I think it's likely that you'll have to add a little bit to compensate for the margins in the dropDownListBushcraft
That is the solution. The problem was font mismatch. The textField and the dropDownList components were having different fonts (so the measurement was different). So I used Arial 12 for the textField (=the font used by the dropDownList)and for the margin I used 10 pixels so it fits. Very neat solution Cameron. I want to extend my gratitude to Shaun also for his efforts and great comments.Beeves
@Rose: Glad it works! I'll remove the part that says "untested" ;-)Bushcraft
Hi @Bushcraft this was the answer i was looking for and thank you for sharing this. BUt i have another requirement that language change. So your code works on different text formats if the language is english. Is there any other way that this should work for other languages like spanis, chinese, etcFrustrated
@subrat: Hmm, I'm not sure why it wouldn't work for non-ASCII characters... if you find a solution, please post it as an answer!Bushcraft
@Bushcraft its very hard to find the solution... its ok if i found any answer then i will share. But to save my time please you should try for some time. thank you for reply.Frustrated
@subrat: I suggest you save yourself the time and find your own solution -- I have other things to do myself, sorry.Bushcraft
M
1

I don't have edit priv on other people's post's so I'm posting this as a separate answer but credit should go to Cameron if this works:

function measureString(str:String, format:TextFormat):Rectangle {
    var textField:TextField = new TextField();
    textField.autoSize = TextFieldAutoSize.LEFT;
    textField.defaultTextFormat = format;
    textField.text = str;

    return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
}

If I see that it does and his is edited I'd delete this one for cleanliness.

Sorry for the garbage post initially was trying to answer the question just did so erroneously... anyhow tested this one and it appears to work. I did this in Flex but you should be able to just use the AS3 part no problem I just wrapped up the textfield in a UIComponent to get it on stage but using the autosize seems to work fine:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <fx:Script>
        <![CDATA[
            import mx.core.UIComponent;
            protected function textinput1_changeHandler(event:TextOperationEvent):void
            {
                // TODO Auto-generated method stub

                var rect:Rectangle = measureString(event.target.text);
                holderBox.graphics.clear();
                holderBox.graphics.beginFill(0xFF0000);
                holderBox.graphics.drawRect(rect.x,rect.y,rect.width,rect.height);
            }

            private function measureString(str:String):Rectangle {
                var textField:TextField = new TextField();
                textField.autoSize = TextFieldAutoSize.LEFT;
                textField.text = str;
                var uiComponent:UIComponent = new UIComponent();
                uiComponent.addChild(textField);
                holderBox.addChild(uiComponent);

                return new Rectangle(0, 0, textField.textWidth, textField.textHeight);
            }

        ]]>
    </fx:Script>
    <mx:VBox>
        <s:TextInput text="" change="textinput1_changeHandler(event)"/>
        <mx:Box id="holderBox"/>
    </mx:VBox>
</s:Application>
Musil answered 7/1, 2011 at 21:43 Comment(6)
but does textField class has validateNow() method?? help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/…Beeves
Doy! sorry got ahead of myself there so used to using validateNow in these scenarios... you're very likely right as TextField is a Flash component not a Flex one, the validation scheme I was talking about applies in Flex components... I have done this with a text field though as well believe you may need to set auto-size or something along those lines... will post an edit above in a few minutesMusil
Interesting... Are you sure it's the autoSize doing it and not the fact that the textbox is being added to the stage?Bushcraft
Honestly not positive I did this on the way out of work yesterday didn't have time to try pure as3 I'll give it another shot today sometime pure as3, very curious now myself. Just know I needed autosize for a preloader textfield to size right.Musil
Thanks Shaun & Cameron. I tested your new code. It only measures the size of the dynamically added textField component but the size of the text in the textInput is different (actually bigger than the textField) u can see this by enlarging the textInput component. The problem is I think, text is represented in different fonts in those two components; but I'm not sure; it appears like that to me. To see that use the same code with <s:TextInput text="" change="textinput1_changeHandler(event)" width="500"/> Thanks.Beeves
Thanks shaun, the problem was font mismatch between the two components. I've put detailed comment on the first post.Beeves
C
0

This is a more polished version of some of the above code. Accounting for linebreaks (html break and \n) and nullifying the created Textfield object with some other optimizations. Hope this is helpful.

function measureString(str:String, font:String="Times New Roman", size:Number=12):Rectangle 
{
    var textField:TextField = new TextField();
    textField.defaultTextFormat = new TextFormat( font, size );
    textField.border = true;
    textField.multiline = true;
    textField.autoSize = TextFieldAutoSize.LEFT;
    textField.htmlText = str;
    // Grab with and height before nullifying Textfield.
    var w:Number = textField.textWidth;
    var h:Number = textField.textHeight;
    //addChild( textField );// This will add the Textfield to the stage so you can visibly see it.
    //if( contains( textField ) ) removeChild( textField );// If it exists onstage, remove it.
    textField = null;//nullify it to make it available for garbage collection.
    return new Rectangle(0, 0, w, h);
}

var str:String = "Jeremy is a good boy.<br>He also has a red bike. \nSometimes Jeremy rides his bike to the store to buy bread for his family.<br>He likes wholewheat.";
trace( measureString( str, "Times New Roman", 25 ).width );

If you prefer this in a class, check it out in my GIT framework: https://github.com/charlesclements/as3-tools/blob/master/net/charlesclements/util/text/TextUtil.as

AS3-tools:https://github.com/charlesclements/as3-tools

Also, our Flash/JS brother Jack Doyle @ GreenSock has some handy stuff to do with manipulating text. Well worth it to check it out:http://www.greensock.com/splittext/

Culpepper answered 7/1, 2011 at 17:27 Comment(0)
C
0

Here is a routine I wrote for Java. I guess you can convert it to Javascript or whatever.

It is not exact, but gives an approximate answer. It assumes that a space is about 200 wide, and M and W is about 1000 wide. Obviously you'll need to scale that based on your particular font size.

static int picaSize(String s)
{
    // the following characters are sorted by width in Arial font
    String lookup = " .:,;'^`!|jl/\\i-()JfIt[]?{}sr*a\"ce_gFzLxkP+0123456789<=>~qvy$SbduEphonTBCXY#VRKZN%GUAHD@OQ&wmMW";
    int result = 0;
    for (int i = 0; i < s.length(); ++i)
    {
        int c = lookup.indexOf(s.charAt(i));
        result += (c < 0 ? 60 : c) * 7 + 200;
    }
    return result;
}
Coincidentally answered 7/11, 2019 at 21:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.