Dynamic Font Replacement: sIFR, FLIR and More
A couple of weeks ago, Antonio Lupetti shared a list of 10 "handwritten" fonts that he uses in his design projects. He also does a brilliant job of integrating that stuff into the diagrams in his posts, which is why I paid attention when he put out a list (just take a look at his archives and you'll see what I mean).
As I was looking at those fonts and downloading them, I was thinking about sIFR, which I've mentioned before. It's a way to replace text in web pages with Flash on the fly, using fonts that aren't on a user's computer. I've wanted to include that on this site and on several others for quite a while.
The problem with sIFR is one that has kept me from using it. See, you have to have a copy of the Flash development tools in order to turn a TrueType font into a sIFR font. You open the little Flash project, change the font and export the SWF. Since I don't do any other Flash development, that seems like very little activity to justify spending $600+ on the tool.
Thus it has remained on the back burner. However, as I was thinking about sIFR, I remembered a site that I had bookmarked recently that used server-side code to generate an image and then did a similar replacement using Javascript, called FaceLift Image Replacement (FLIR).
So, last night, I installed it and wrote a quick test script to see how well it might work with the handwritten fonts and a couple of others. There's a config file in FLIR (config-flir.php) that has a list of the "installed" fonts that FLIR knows about. That $fonts array is what the while loop in the sample below is going through.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Headline Font Test</title>
<script language="javascript" src="flir.js"></script>
</head>
<body>
<?php
include("config-flir.php");
while (list($font, $value) = each($fonts)) {
?>
<div style="width: 770px;">
<h1 style="font-family: <?php echo $font; ?>, sans-serif;font-size: 40px;">J Wynia - www.wynia.org - The Glass is Too Big</h1>
<i>Font for the above: <?php echo $font; ?></i>
</div>
<?php
}
?>
<script type="text/javascript">
FLIR.init( new FLIRStyle( { mode:'wrap' } ) );
FLIR.auto([ 'h1' ]);
</script>
</body>
</html>
So, I ran that script and the results weren't what I had hoped, but were what I expected. You can see for yourself in a PDF copy of the results.
Basically, what appears to happen is that each letter is rendered individually with a background around the actual text. That works fine for fonts where each character stays neatly separated and is part of why I wanted to try this with handwritten fonts, which are more likely to intermingle.
In most of the handwritten fonts, you can see that where they overlap, letters that follow others and would normally overlap them cover the preceding character with a white rectangle. That rectangle eventually gets turned into transparency in the resulting PNG, but is still there, cutting a notch into the preceding character. [P.S. If you like the "imitation" font, but wonder why it's got "m"s all over the place, it's a demo. The real one is from Harold's Fonts, and can be purchased from Font Bros for $20]
OK. That means that FLIR is still usable for nice, blocky fonts in titles. But, I wondered if .NET's text rendering suffered from the same problem as GD2 in PHP (and the problem is mostly in GD2 and not PHP itself).
So, I hit Google, found this guy's sample method and whipped up a console app that spit out the "same" PNG as the FLIR code.
I put both images into a test HTML page to compare. I put them over both a white-ish background and the pattern from this site to see how the transparency compared. Both were the same. It was on the intermingling, however, that I was pleasantly surprised.
The C#/.NET image clearly shows the letters "interacting" and overlapping the way that the font designer intended, not stepping all over each other. Encouraged by that, I wondered how feasible it might be to do a back-end swap from the PHP/GD2 powered solution.
What the Javascript is doing in FLIR is doing a bunch of calculations, including how much space is available, what font should be used, what size, etc. Most of that is serialized to a JSON object and passed to a script called generate.php.
So, I set up an ASP.NET website to do a proof of concept/prototype. As always, a disclaimer about POC code. This is a bare bones proof of concept to test out the feasibility of doing this. It is in no way a completed implementation. The PHP backend for FLIR is quite extensive and does a lot of detail work based on that JSON object. This one just takes the text string from the URL and renders it on one line.
That said, I included 2 ASPX pages. The first actually was needed because the eventual bits that choose the font use the font family "name" as the lookup. However, it wasn't entirely clear for a couple of my fonts what that name was.
So, FontList.aspx is a barebones page, with a GridView control dropped on it and the following code in the codebehind.
Get the list of current font names
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Drawing.Text;
using System.Drawing;
public partial class FontList : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
System.Drawing.Text.InstalledFontCollection RawFontList = new System.Drawing.Text.InstalledFontCollection();
ArrayList FontList = new ArrayList();
foreach (FontFamily family in RawFontList.Families)
{
FontList.Add(family.Name);
}
GridView1.DataSource = FontList;
GridView1.DataBind();
}
}
Hitting that page will give you a list of all of the fonts available on your system for use in generating images.
The second page I called generate.aspx, mostly for eventual compatibility with the FLIR front end, to change as little as possible. Now that this prototype is done, I think I'd actually implement an HTTPHandler to just handle requests to generate.php, so NO changes would be necessary in the Javascript.
At any rate, that page needs the ASPX side stripped of all of the HTML, leaving only the top line as a pointer to the codebehind. In the CS file, I borrowed from the earlier C# code, tweaking it to get better quality on the smoothing and hinting as well as hard-coding the color to black.
I tried to comment fairly heavily to make the code clear.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.IO;
using System.Web.Security;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//This is a quick prototype to test the font rendering of
//C#/.NET against the PHP/GD2 rendering for a possible
//replacement of the PHP backend of FLIR*.
//*http://facelift.mawhorter.net/examples/
//Grab the text for the image
String InputText = Request.QueryString["text"];
//See snippet in the article to get the list of names for fonts on your machine
String FontName = "Gregs Other Hand";
int FontSize = 45;
String SavePath = HttpContext.Current.Server.MapPath("~/App_Data/");
//Get an md5 hash of our input (just text in this case).
//That provides a nice way to know that a given cached image
//was generated from the same inputs and is safe to return
//instead of creating from scratch.
//No such caching implementation exists in this prototype.
String BaseFilename = FormsAuthentication.HashPasswordForStoringInConfigFile(InputText,"MD5");
//Actually do the image creation
Bitmap HeadlineImage = CreateBitmapImage(InputText,FontName,FontSize);
//Save to the hard drive. A real implementation would check here
//before creating the image, delivering the cached one instead.
HeadlineImage.Save(SavePath + BaseFilename + ".png",System.Drawing.Imaging.ImageFormat.Png);
//Save to a MemoryStream so we can output the raw image to the browser.
MemoryStream stream = new MemoryStream();
HeadlineImage.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
//Because we're not outputting HTML, we do some modification of Response
Response.AppendHeader("Content-Type", "image/png");
Response.OutputStream.Write(stream.ToArray(), 0, (int)stream.Length);
Response.End();
}
private static Bitmap CreateBitmapImage(String sImageText, String FontName, int FontSize)
{
//Images all start as "bitmaps" in .NET. This one starts at the top corner.
Bitmap objBmpImage = new Bitmap(1, 1);
int intWidth = 0;
int intHeight = 0;
// Create the Font object for the image text drawing.
Font objFont = new Font(FontName, FontSize);
// Create a graphics object to measure the text's width and height.
Graphics objGraphics = Graphics.FromImage(objBmpImage);
// This is where the bitmap size is determined.
intWidth = (int)objGraphics.MeasureString(sImageText, objFont).Width;
intHeight = (int)objGraphics.MeasureString(sImageText, objFont).Height;
// Create the bmpImage again with the correct size for the text and font.
objBmpImage = new Bitmap(objBmpImage, new Size(intWidth, intHeight));
// Add the colors to the new bitmap.
objGraphics = Graphics.FromImage(objBmpImage);
// Set Background color to transparent for the PNG
objGraphics.Clear(Color.Transparent);
//Set the smoothing and hinting to high quality
objGraphics.SmoothingMode = SmoothingMode.HighQuality;
objGraphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
//Actually put the string onto the image
objGraphics.DrawString(sImageText, objFont, new SolidBrush(Color.Black), 0, 0);
objGraphics.Flush();
//We have our image and can return it to the calling scope.
return (objBmpImage);
}
}
The results are quite nice. Obviously, it doesn't do the nice FLIR things like line wrapping, handling colors, handling mixed fonts, etc. However, it does render a nice, clean image of the text, using whatever TTF I throw at it, without overlapping.
If you wan the entire project, you can download the ZIP file. I'd love to see someone run with this.
[edited by J: Oh, and one last FYI. If you decide to install fonts during this process, IIS needs to be restarted to pick the new fonts up. Strange, but it happened to me so, heads up!]



August 31st, 2008 at 2:56 pm
Just found your post via twitter.
"Basically, what appears to happen is that each letter is rendered individually with a background around the actual text. That works fine for fonts where each character stays neatly separated and is part of why I wanted to try this with handwritten fonts, which are more likely to intermingle."
FLIR includes the FancyFonts plugin which uses ImageMagick to create a much more pleasing result. ImageMagick does not suffer from the same limitation and I've used it in combination with the Bleeding Cowboys font and a number of handwriting fonts.
August 31st, 2008 at 3:59 pm
Somehow I missed that FancyFonts plugin when I was looking at FLIR. It's another good option, though I know I've had issues where ImageMagick won't run on hosting services I've used.
I'm wondering if turning my prototype into another back end to FLIR would just expand the possibilities. Same front end hitting either the PHP/GD2, PHP/ImageMagick, C#/GDI, etc. back end to generate the image and handle the caching, etc.
September 1st, 2008 at 12:21 am
I would LOVE if someone created an ASP.NET port of the code. Your code is interesting and seems like a good base for a complete port of the facelift backend.
I found a couple of pretty complete .net scripts that took their inspiration from the A List Apart method. I made a post on my forum about it [ http://forums.mawhorter.net/viewtopic.php?id=38 ].
Keep me informed if you do more work on your code. I would be more than happy to post a link to a ".NET distribution". I'd also love to see someone run with this.
December 7th, 2008 at 2:34 pm
[...] public links >> iis WordPress 2.6.1 is out Saved by Jorob on Fri 05-12-2008 Dynamic Font Replacement: sIFR, FLIR and More Saved by memeslayer on Sun 16-11-2008 IIS 7 Podcast Saved by pokepoke on Thu 13-11-2008 Adding [...]
December 19th, 2008 at 6:37 pm
I too would like to see a proper version of FLIR that works with ASP.NET / C#. I'd do the port myself if I could.
April 2nd, 2009 at 1:21 pm
Wow - is there any way someone could take this thing to completion? I'd gladly pay $$ for a .NET library that does just what FLIR does. FLIR absolutely rocks in terms of functionality, but I can't really run/manage a PHP site in our infrastructure.
Please?
Pretty please?
April 7th, 2009 at 3:43 pm
How much are we talking here? ;]
I have .net experience and would be more than happy to create a duplicate of the PHP backend.
If you're interested, you can head over to my website and get ahold of me there or in the FLIR forums at forums.mawhorter.net
May 20th, 2009 at 5:34 am
[...] use FLIR, we would have our solution. But FLIR is not an option. Nonetheless I've found an ASP.NET version of FLIR. I passed this on to Bert Vissers, our main developer. He did some tests, and the image output [...]
May 28th, 2009 at 11:23 am
[...] public links >> iis WordPress 2.6.1 is out Saved by Jorob on Fri 05-12-2008 Dynamic Font Replacement: sIFR, FLIR and More Saved by memeslayer on Sun 16-11-2008 IIS 7 Podcast Saved by pokepoke on Thu 13-11-2008 Adding [...]