WP7 Small-text Tiles with PHP
Anyone who has worked with Windows Phone tile notifications should have found that while it’s great to be able to put text on the back of them, the font is very large and hardly useful for anything more than a few words. It would be great if we could easily choose a small font size, similar to the one used on the Me and contact tiles.
The solution presented below is not the easiest of methods, but it is a way to meet an identical replication of the Me tile back-side. You will need a back-end infrastructure running PHP (though I’m sure you could easily port it to .NET — if you do, please let me know and I’ll feature it here). If you already have a Linux server pushing notifications then you’ll be able to set this up in no time — if you don’t then I highly recommend you look at setting one up, because notifications done well from a server are so much more battery and resource efficient for the user than notifications created via a scheduled task.
I have spent a lot of time figuring out the exact margins, font-size, spacing, etc to make this look 100% native. It is important that we create a transparent PNG because it will then let the accent colour show through. I see plenty of apps with tiles created on-device using JPG libraries, and these have two major flaws:
- JPG creates heavy artifacts with high contracts items like fine white text on colour
- JPG doesn’t have transparency, so to get around this the app detects the current accent colour. If the user changes their accent colour then the JPG tile is incorrect until the background agent wakes up.
When creating PNG tiles on the server we get a 100% native look and feel, which cannot be understated when working on the Windows Phone platform.
Prerequisites:
- A web server running Apache & PHP
- A WP7 app with working tile notifications
- The Windows Phone SDK (for the Segoe WP font)
Setup
To create tile backgrounds with small text we’re going to need to generate an image on the server-side. This can be done via the GD library, or another library such as ImageMagick. I’ve gone for the latter, simply because the syntax is a lot cleaner for creating transparent PNGs and for implementing manual word-wrap.
Let’s install ImageMagick. On Ubuntu you can do this with:
|
1 |
$ sudo apt-get install php5-imagick |
Other distributions will be similar, and PHP on Windows shouldn’t be too hard.
You will then want to copy the Segoe WP font from the C:\Windows\Fonts directory on one of your WP7 development PCs. It’s copyrighted so I can’t host it here, but you will get it bundled with the Windows Phone SDK. Copy this to the same folder that you will create the back-tile script in below. You’ll only need the regular style: SEGOEWP.TTF
back-tile.php
Choose somewhere within your www folder to place back-tile.php. Using vim or your favourite editor, paste the following code:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
header('Content-Type: image/png'); // Get all caching stuff ready $image_cache_dir = __DIR__.'cache/'; $image_cache_filename = $image_cache_dir.'tile-back_'. md5($_REQUEST['text']); if (file_exists($image_cache_filename)) { // serve cache $fp = fopen($image_cache_filename, 'rb'); fpassthru($fp); exit; } // else if (!is_dir($image_cache_dir)) mkdir($image_cache_dir, 0644, true); $im = new Imagick(); $im->newImage(173, 173, 'none', 'png'); $draw = new ImagickDraw(); $draw->setFont('SEGOEWP.TTF'); $draw->setFontSize('20'); $draw->setFillColor(new ImagickPixel('white')); $max_width = 173 - 24; $words = explode(' ', $_REQUEST['text']); $lines = array(); $current_line = ""; foreach ($words as $word) { if ($current_line == '') { $test = $word; } else { $test = "$current_line $word"; } $metrics = $im->queryFontMetrics($draw, $test); if ($metrics['textWidth'] > $max_width) { if ($current_line == '') { $lines[] = $test; $current_line = ''; } else { $lines[] = $current_line; $current_line = $word; } } else { $current_line = $test; } if (count($lines) == 5) break; } if ($current_line != '' && count($lines) < 5) $lines[] = $current_line; $y = 27; foreach($lines as $line) { $im->annotateImage($draw, 12, $y, 0, $line); $y += 23; } $im->writeImage($image_cache_filename); echo $im->getimageblob(); ?> |
Not only will this script generate your back-tile image, it will also cache it (going by the input string) for further use. This means that if you’re pushing the same tile out to multiple users it is only generated once, and future requests pass the cache right through.
You can run the script with:
http://yourdomain/your/path/back-tile.php?text=Text to go on the back of your tile
This is white text on a transparent background, so you’ll likely see nothing when viewing in-browser. However if you save the image and load it in an image editor (eg Photoshop) then you’ll be able to see if it worked or not. You should have a tile with up to five lines of small text, with a blank space left at the bottom for your BackTitle parameter (usually the name of your app or tile).
Why not use ImageMagick’s built-in word-wrap? I tried this first, but found that the line spacing was much too large. The Imagick library for PHP doesn’t have a line spacing option so I had to use the above code which I had found on a very helpful blog post regarding basic word wrapping with Imagick.
Sending the notifications is beyond the scope of this post, but suffice to say that you can push this tile back using data like so:
|
1 2 3 4 5 6 7 8 |
$data = '<?xml version="1.0" encoding="utf-8"?>'."\n". '<wp:Notification xmlns:wp="WPNotification">'."\n". ' <wp:Tile Id="TileUriPath">'."\n". ' <wp:Count>'.$count.'</wp:Count>'."\n". ' <wp:BackBackgroundImage>'.xml_safe_string('http://yourdomain/your/path/back-tile.php?text='.urlencode($text)).'</wp:BackBackgroundImage>'."\n". ' <wp:BackTitle>'.$tile_title.'</wp:BackTitle>'."\n". ' </wp:Tile>'."\n". '</wp:Notification>'."\n"; |
Where xml_safe_string is a function that replaces the restricted characters "<>'& with their (ht|x)ml entities.
Finally, the very last change to make is in your application itself. You need to change the ‘BindToShellTile’ call to approve yourdomain for access, otherwise these notifications will be flat-out ignored by the OS.
|
1 2 |
_channel.BindToShellTile(new System.Collections.ObjectModel.Collection<Uri> {new Uri("http://yourdomain/", UriKind.Absolute)}); |
That’s it! A workaround requiring a bit of time to set up, but it is definitely worth the result. For all we know Windows Phone 8 may remove the need to do this at all, but only time will tell.
Did this work for you? Let me know in the comments. If you use this code in an app I’d love to hear about it!