Originally published on: 8/22/2008 7:41:26 AM
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.
[sourcecode language='php']
[/sourcecode]
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.
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
[sourcecode language='csharp']
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();
}
}
[/sourcecode]
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.
public partial class _Default : System.Web.UI.Page
//Grab the text for the image
private static Bitmap CreateBitmapImage(String sImageText, String FontName, int FontSize)
// Create the Font object for the image text drawing.
// Create the bmpImage again with the correct size for the text and font.
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;
{
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/
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();
}
{
//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;
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;
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.
[caption id="attachment_1111" align="aligncenter" width="300" caption="Sample ASP.NET Generated Headline"]
[/caption]
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!]
"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.
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.
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.
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
Please?
Pretty please?