Monday, December 7, 2009

How to Get Character Widths for a Browser's Font Size

A few days back, I blogged about "how to force string wrapping" for mobile device display. In order to know when to break a string of text, we would like to know how many characters of text fit into any given html element on the page. Simple right? If you know an element's width, as I usually do since I use a fixed width 'body' div element and can make a reasonable guess as to what each child elements width will be, all I need is the width of each character. That way, I can find the number of characters per line for that particular element and break accordingly.

So how do you find the width of each character? Now we have a problem. There is no way to know before a page is rendered what the width of a character is for a given font, font size, font style, etc. in a given user's browser. Remember, we just specify what font and font size to we want them to use. The browser can and will use whatever font and font size it deems appropriate. This is especially true for mobile devices, which vary greatly in screen size, available fonts, available font size and browser software.

That said, in many of my scripts I use a set character width vs font size relationship hash:

my %character_width = (
 'font_large' => 13,
 'font_medium' => 10,
 'font_small' => 8,
 'font_x_small' => 6,
 'font_xx_small'=> 5,
);

...where 'font_large' is the character width in pixels for a style specification of font-size:large, 'font_x_small' is for font-size:x-small, etc. This actually works reasonably well in many cases. Sometimes your text will push outside it's container element, but on average it looks ok.

But if you want a more accurate measure of character width for each user, you can try this technique. What we do is render a page and let JavaScript tell us what character width is being used for each font size. Here is the html we use in the polling page:
....
<div>
<span id="xl" style="font-size: x-large;">Open Source</span>
    <span id="lg" style="font-size: large;">Open Source</span>
    <span id="md" style="font-size: medium;">Open Source</span>
    <span id="sm" style="font-size: small;">Open Source</span>
    <span id="xs" style="font-size: x-small;">Open Source</span>
    <span id="xx" style="font-size: xx-small;">Open Source</span>
</div>
....

Pretty basic. The surrounding div is usually set to style='visibility:hidden;', but I left it visible here for demo purposes. As you can see, just a simple line of text of known character length (11 characters in 'Open Source') for each font size span tag. And here is the JavaScript which sends character width information to your script:
....
function init_page(){
  var size_output = '';
  var site_url = 'http://your.website.com/cgi-bin/your_script.cgi?';
  var width_array = [ 'xl', 'lg', 'md', 'sm', 'xs', 'xx' ];
    
  for(var i=0; i<width_array.length; i++) {
    size_output += '&' + width_array[i]
      + '=' + get_character_width(width_array[i]);
  }
size_output = size_output.substring(1);

  // Send info to your script...
  window.location = site_url + size_output;
}

// Helper functions...
 
function get_character_width(el_id) {
    var output = 0;
    var element = document.getElementById(el_id);
    var text = element.innerHTML;

    if (typeof element.clip !== "undefined") {
       output = element.clip.width || 0;
    } else {
        if (element.style.pixelWidth) {
            output = element.style.pixelWidth || 0;
        } else {
            output = element.offsetWidth || 0;
          }
      }
    output = round_up_down(output/text.length, 0);
    
    if (output < 5) output = 5;   // practical limit is 5px per character
    return output;
}

function round_up_down(number, num_of_dec_places) {
    var num_to_10th_pow = parseInt(Math.pow(10, num_of_dec_places));
    return number = Math.round(number * num_to_10th_pow) / num_to_10th_pow;
}
....
Use the above in a 'polling' (index.html) page which loads the above JavaScript at page load. I use the Dojo Tookit, so I left that stuff out, but a simple <body onload="init_page();"> will work just fine. So the above JavaScript gets the overall size of each span of text, which has been set to each specific font size, divides it by the number of characters and voila, we have the character size in pixels. Cool eh? I just add this new info to my redirect uri string and it is sent to my script.

Now you might still be asking why this is important. Well as mobile sites become more advanced, there will be more info displayed from general databases and such. Since this data might not already be conditioned for mobile display, the above technique, among others, may become the norm and not the exception.

Here is an example of it in action. Go to my website: http://opensite.mobi. Watch as it first goes to our index.html page. Quickly it redirects to our site. Now look at the redirect uri in your browser's navigation line. I send the info bundled (I write a cookie to store this), but you will see something like 'rev_cookie=unknown:js_yes:14:10:9:8:6:6' if JavaScript was enabled. The numbers part is what we are interested here. Largest to smallest, we now have the character sizes as gathered for this browser. Note: on my website, once a cookie is written the index.html page skips the character size stuff, so you will have to clear cache to see it again if you try multiple times.

If you know of similar or other data preparation techniques for mobile display, please post a comment with code or a url. I searched a good bit some time ago, but you never always know what's really out there.

No comments:

Post a Comment