At the web agency I work at, it is important that when a designer is laying up a site, they are not held back by the limitations of HTML. Of course, at the same time, the technical team still needs to produce an accessible website in HTML! One of the classic design limitations has always been the varying ability to embed non-standard fonts. Mainstream browsers have had the ability to embed fonts since IE4 and Netscape 3.5, but this support has been non-standard, ad-hoc, and inconsistent. As such, there is no easy way to achieve cross-browser and cross-platform font embedding. This restricts designers to either using Verdana, Arial, and Helvetica with Sans-serif as the fallback, or having to produce multitudes of images - one for every heading. Of course, this increases maintenance costs when your client wants to change or add a heading as you need to get a designer to fire-up Photoshop just for, effectively, a text amend. In aid of simple maintenance, we wrote this dynamic headings control.
Using the code
The whole purpose of this control is to make things simple so once you have completed the configuration, the page usage should be trivial. This is an example of the usage:
text="Skulls are cool"
We've registered the control in our page (or you could globally register it in the web.config) with the prefix "tm", and we've created a style in the web.config, as shown below.
As you can see, we've registered a font here and then created a style that uses this font. The name of the style we have used is the one used in the
headingstyle attribute of the web control. The
name attribute in the
font element should be the name of the font you are using. This is not the name of the font file, but rather the actual name, which you can find out by opening the font in a font viewer (this must be exactly as shown, otherwise it won't work).
You can register as many fonts and as many styles as you want, and you can use these as many times as you want on a page via a control. I've tried to make the options as close to that of CSS as possible without making it confusing. I've also added the ability to set a heading level so that heading tags will also be rendered on the page.
How it works
Here is the output generated from the above configuration:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<html xmlns="http://www.w3.org/1999/xhtml" >
<title>Skulls heading example</title>
<form name="form1" method="post" action="default.aspx" id="form1">
<img alt="Skulls are cool"
As you can see, an image tag is created and the
alt text is populated with the requested text. The
alt text will always exist and be the text used in the
text attribute (this erases the possibility of copy and paste errors where the text doesn't match the image). All of the information required to generate the image is in the URL. The text for the image is stored between the last "/" and the ".aspx", and the folder name before that is the style to be used.
The page is created in one request, and then requests for these image files are handled by an HTTP handler also registered in the web.config. This handler splits out the style and text, then requests the style from the style store. Using the style details, an image is then generated and returned to the browser.
I hate doing things twice, and to me, that is one of the attractions of programming: to do something once, properly, and then use it again and again. As such, I didn't want the fonts to be loaded again for each image request, so I needed some level of persistence. I could have used the cache, but instead went for a singleton which contained the font and styles, which I've called
StyleStore. The constructor of
StyleStore loads up the fonts and styles, and because static classes are stored in the application level, this will only occur once for each application. Any time a new font or style is added, the application is restarted, so this will cause the fonts and styles to be reloaded as well.
Other than this, the code is not terribly difficult to follow. The image rendering can be found in many examples across the net and CodeProject. The control is a fairly standard web control, although I've steered away from using the viewstate for simplicity' sake (and personal preference).
I decided to throw as few exceptions as possible because hopefully the system will be easy enough to configure for users. Also, configuration exceptions aren't the easiest to track. If you get black on white Verdana images, then you have configured something wrong; you will, however, get an exception of "no font file found" if the location specified is wrong.
I've also implemented custom configuration sections with collections, if you are looking for an example of how to use this nice feature.
Getting the example going
You will need to have the ASP.NET web application projects update installed to use the provided project file (free Microsoft download). Once you have that, you will need to set up a virtual directory and point it to the file you've unzipped to. Open the web.config file and change the font directory paths as appropriate. The four fonts provided are available free from 1001 free fonts.
In an ideal web world, all content would be text, which would allow for unlimited end user customisation. In light of the inability of web browsers to embed custom fonts and the limited array of web safe fonts, this control fills the gap between ease of maintenance, designer and client demands for prettier sites, and web accessibility through the dynamic generation of image headings with enforced alternate text and optional heading tags.
Trunk Media retains the copyright of this article and code. The code is licensed under the Lesser General Public License (LGPL), which means you can use the library to create your headings by linking to it. It'd be nice if you link to our site if you do. As I understand the LGPL, you can also recompile the code and add or remove any features you want or don't want, or fix bugs, as long as you release the code back to the community and retain the copyright headings in the code.
- The custom configuration section uses features of ASP.NET 2.0, so it isn't easily backwards compatible to 1.1
- There is no text-wrapping ability, although this could be added without too much difficulty.
- The GDI way of measuring fonts, especially custom fonts, is not terribly accurate. I've achieved satisfactory results by adjusting the padding on each style (you can use negative padding).
- The images that are created are PNGs, this is so that we can use transparencies easily, and because they are a nice, open standard image format. You may need to incorporate the PNG fix for transparencies if you are building for IE6.
Images are not currently being cached, which would be essential on a high hit website.
- Font sizes aren't necessarily consistent across fonts. Check sizing in a font viewer first, or just play around; this is due to font design, not a limitation of the code.
- There is currently no way of constraining the image dimension. This could also be added without a lot of difficulty.
- No background images, but again, this could be added.
With the addition of the caching functionality, it would now be possible to generate a unique key for each unique image, which could be used instead of passing the style and text values in the URL string. This would also facilitate the other feature I'd like to implement, which is writing the images to the hard drive and then referencing them through the web server directly. Stay tuned, or feel free to contribute.
In light of the short comings of browsers and fonts, this control has been designed to allow users to create image replacements which are easily configurable and have the additional benefit of protecting the font copyright holder. Check out the example project, and hopefully this will better explain the features and abilities of this control. I appreciate all constructive feedback.
- 17 September: Version 1.2 - Added the config checking page. This is accessible from the HTTP handler folder + "/dynamicheadings/checkconfig.aspx". This will print out any errors that have occurred in the processing of the web config section.
- 24 April: Version 1.1 - Added simple caching, made the exception serializable, added in a string length limitation default (which is adjustable), and added a settings class for control wide settings. Thanks for the feedback Ramon and Kevin.
- 23 April: Version 1.0 - Enjoy!