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.

Thursday, December 3, 2009

Click here for Mobile Website Development

I've been surfing the web for a long time, looking for articles on mobile web development and design. Tons of articles, some good - some just the same old rehash.

Anyway, I came across this url today and it is the best reference to good quality mobile development articles I have seen yet. Have a look if your are new to the game:


One stop shopping on the web...

Tuesday, December 1, 2009

How to force wrap of long strings for mobile display

One problem I came across a while back while developing scripts that generate pages to be viewed by mobile devices was that long continuous strings of characters would not wrap 'correctly' (at all). This was made more noticable since I tend to use width defined html tags. For example, url's you want to show directly in html may often be wider than the screen that will be viewing them. Now you might say, so what. They just extend past the current viewing window. With mobile devices, this is not an optimum situation for efficient browsing.

When designing a mobile site, it is best not to make the user scroll any more than they have to. And not making the user scroll horizontally is even more important. I believe some browsers don't even allow it, (newer mobile browsers are more capable). And this problem actually happens quite often when you output url or file path strings from within your scripts. Especially when outputing strings for debugging or error reporting.

So what to do?

Well, I wrote some Perl code to force breaks in long strings of characters or in certain long words within paragraphs of text which wouldn't wrap to my liking.

Here is the code:

sub clean_breaks {
# -----------------------------

my $string = shift;
my $cl_break = shift;
my $filter = shift || '\W';
my $line = 0;
my $word = '';
my @words = ();

@words = split / /, $string;

foreach $word (@words) {

my $more_word = '';
my @more_words = ();

if(length $word > $cl_break) {

$word =~ s/(.{$cl_break})/$1 /g;
@more_words = split / /, $word;
foreach $more_word (@more_words) {

if(length $more_word > ($cl_break/2)) {

$more_word = reverse $more_word;
$more_word =~ s/($filter)/$1>\/ rb</;
$more_word = reverse $more_word;
unless ($more_word =~ m/<br>|<br \/>/) {
$more_word =~ s/^(.{$cl_break})/$1<br \/>/;
}
}
}
$word = join ('', @more_words);
$line = 0;

} else {

$line += length $word;
if ($line > $cl_break ) { $word = '<br />' . $word; $line = 0; }
}
}
join (' ', @words);
}

It works by breaking up long strings of text, base on the screen width of the user's display, times the html tag's % of screen width for that paticular tag (ie: 100%, or 1 for the body tag), divided by the width of each character. I work in pixels, but the units aren't important when the principle is respected.

So what you want is the approximate number of characters per line where normal wraping occurs, for that particular tag. And yes, the user's screen width and character width values will have to be guessed since they will vary by the user's browser, device and user settings. More on that another day.

So, you enter a string of text (or a string of continuous characters) as the first input to the subroutine '&clean_breaks' and then a 'characters per line' integer as the second input. Run the subroutine and voila, you get back your original string but with tags inserted in strategic places to force wraping in the designated tag. And it does work. Here is a link to a working example:


Run the above script in a mobile browser and look at the output. This script reports back the 'user agent' string it detects, as well as some WURFL data inspired strings. These strings represent the 'continuous character' strings I talked about in the beginning of this post. Normally, they wouldn't wrap and they screwed up the look of the outputed page. And here is a link to the actual script (just in case the above doesn't work after copy/paste):


I scanned the web a while back and couldn't find anything else available at the time. If you know of something similar, drop me a line or leave a comment. I'm always on the lookout for improvements.