Background

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:

<tm:dynamicheadingid="Dynamicheading1"text="Skulls are cool"headingstyle="skulls"runat="server"/>

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.

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.

The code

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.

Accessibility

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.

License

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.

Limitations

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.

Future improvements

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.

Summary

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.

History

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.

Share

About the Author

I'm currently technical director at Trunk Media, an Auckland,New Zealand based interactive web agency specialising in accessible and usable web development. I've had several years experience working in London,England working for a top web agency whose clients included several FTSE 100 companies. I've been developing websites for over 8 years now and have a broad range of skills I'm constantly trying to improve.

When I'm not flat out at work I'll be cross country mountain biking, trying to snowboard, swimming in the surf or fiddling with an old car or two.

Great code, although i can't get it working yet, but where can i get version 1.2 ? The version here is 1.0.0 ? I am getting the error FileNotFoundException: File not found, although i have double checked everything and all my paths are absolute and i can access the files fine. I have allowed asp.net, network service, system, IUSR etc permissions and i am having no luck.

Glad you liked the code. It's been a while but I'm pretty sure I uploaded the update over the old one so that should be the latest code. In terms of your file not found error, I can only speculate. I've used this on a couple of projects and never had a problem with not finding the file so I'm wondering if it is a path issue I've never had. Being an previous linux user I never have spaces in my directories, perhaps this is an issue I haven't tested for? One thing that is worth doing is downloading sysinternals filemon (now owned by microsoft) and see where it is looking for the font, that will quickly tell you what the problem is. Also, from memory, due to the caching, you need to restart the app to have config changes take effect.

thanks for the quick reply, i shall try all that you have said and report back if there is anything that i find that is due to spaces etc. Not sure about the version on here, looking at the file in VS shows version 1.0.0.0.

I have found a problem when I have to specify a hosting server font directory paths!

Localy all works, but the problems borns when I have the need to specify a different path (relative to hosting server).

I try to specify a relative path (./path/fontname.ttf) and also an absolute path (http://www.sito.com/path/fontname.ttf), but also other kind of combination.
I'm worn out , I don't know other think about.. so if is possible I ask a your help!

Hi Maveryk. You'll need to specify the absolute path. You can find this pretty easily by using Server.MapPath() and writing that to screen and then copying the value to your config file (if you don't know it).

I actually tried this solution too. I've put the script statement at the end of the page (between form and body end tags) to no avail. Did you modify anything else? How did you get the height and width of the image to display in the image tag (that is created from the control)?

Hello,
the code you are using is great, I try it on my local pc it worked successfully, but when i tried the code on a server, the image text was not written correctly, all the words where transformed into "WWW" i didn't know why though on the local pc it worked fine
so is there any requirements needed for the code to run on a certain servers maybe i am missing something
if you can help me i will be gratefull
Thanks

Try viewing the source and copying the url of the image and paste that into a new browser window to see if you are getting the image that way. I'm thinking that you may not have changed the font path to the font file in the web.config to match the live location.

I've added a configuration check ability to see if your systems is configured correctly and I have uploaded these files over the old ones. Once you download the new files and installed on your site you should be able to call the address http://www.annaharkw.com/annaharTest/images/dynamicheadings/checkconfig.aspx. This should write to the screen any configuration issues you might have in the web.config.

It seems most likely to me that the web.config is where the error is occuring. You could additionally try setting the padding on the image style to very large so you can see what other text is being written out in the image.

First, Using images increases the bandwidth usage which is pretty relevant on most high traffic website. Second, using images isn't good for accessibility. If your company is good in webdesign then you should at least obey the standards by supplying at least an ALT attribute to the IMG tag.

In short.. this really isn't the way to go.

Better would be to have a javascript based version that dynamically replaces the appropriate tags with the above code. This way the original document can comply with all standards.

But I think that isn't even the way to go. Dynamically creating headings/logo is bad for the cpu thus requires a more static approach.

Some coding tips:
- The exception class is not really helpful and should at least be serializable.
- try..catch is too generic.. catch explicit exception types

Additional logic:
- Would your webserver find it neat if I ask it to render a string of 1000 characters?
- Add a referrer check so only request of your own domain will be rendered
- What about font-size?
- Create a file-based cache

Yes, you need to ensure that ALT tags are used for accessibility and I would recommend caching the images.

While a designer should plan their designs based on Web Safe Fonts for such dynamic parts such as headings, there are times when a designer will opt for a "cool" Font, most likely a Mac only Font In those cases the developer will need to render all headings (or whatever) using this Font to create an Image for use on the Web.

I had a case a while back where the designer did just that. They were told to keep all dynamic portions of the site web-safe but they opted for a non-web-safe font for all page headings. I found a similar web-safe font and replaced all headings using CSS but the designer pointed out my change to the site owner and I was forced to use images (with ALT tags) for all headings

Using non-web-safe Fonts is a bad idea for dynamic portions of a web site. As long as the web site has a finite set of pages, you may be okay but as a developer, you must always think about growth. To allow for dynamic content and pages (possibly via a CMS) the developer will need a dynamic means for generating these images.

The solution presented here is a good one but as Ramon suggests, it does need some fine tuning.

For the most part, dynamic headings or other elements will most likely not change except when there is a change to the page, thus, a new heading could be generated and cached upon change and used until the next update to that page.

I believe that the above suggestion is best for CMS based systems which I assume is what the author here is referring too, otherwise, dynamically generated headings are overkill.

That being said, taking all of these and Ramon's suggestions into account, this code could be made to work as part of a CMS solution to work quite nicely.

You could possibly even create a DotNetNuke module using such a solution... now that would be cool

Thanks for your feedback. I knew there would be people out there that have had the same problem as we have had. I know I've had it before and as much as you want to convince designers that that all important font is not all that important sometimes it just needs to be done.

The alt tag is mandatory in this control, you can't get an image without it being rendered. Maybe I need to make this more obvious in the article?

If time allows, I will certainly be adding caching or file writing as per the list of limitations.

I had hoped by putting the code out there that someone will find it useful enough to put it into DotNetNuke or similar CMS, as I think that would be really cool too, you could get some great themes going!

Thanks for you feedback. To cover off your points:
1) Firstly, the alt tag is rendered and it is rendered exactly as the text that is entered. I've been building accessible sites for about the last 5 years but sometimes clients and designers really want that special font for whatever reason. Personally I'm pretty happy with verdana and arial as they're both easy to read. One of the problems I've noticed with maintenance on large sites is that people get sloppy when copy and pasting image headings and using this technique prevents any possibility of having the wrong alt text matched up to the image wording which makes for a true accessibility nightmare.
2) I like the idea of a javascript option but I don't agree that the document doesn't comply with standards. I don't think you will find anywhere that says that you cannot use images for headings, they just need to have alt text (which this control enforces) and you need to wrap the image in the appropriate heading tag (which is optional in this control with the headingLevel attribute). This control also allows you to pass in class and style attributes to the heading tag if need be.
3) I agree that dynamically creating headings is not ideal but as I stated in the limitations the system is not yet cached but I felt it was important to put something out there for the community and improve on it with feedback, time limitations.
4) Try catch was kept generic as I wasn't passing on any of the exceptions. You either got it right or you didn't. I found this was an easier of ensuring you always got something out of the pages.
5) Rendering 1000 characters. Again, I put this in my limitations and it would be trivial to implement to put a maximum of 100 characters or less but I thought I might wait for some more inspiration or suggestions on the cleanest way of dealing with this.
6) I don't think referrer checks are a particularly useful way of dealing with bandwith stealing or cpu stealing. Any sort of caching or writing images to the hard drive would make this unnecessary (although bandwidth stealing is always a problem on any big site and there are many ways of dealing with this).
7) Font size was the first thing I implemented. What were you referring to here?
8) File based cache is mentioned in the limitations and I will be implementing this as time allows. I personally wanted the option in the configuration of writing to disk and serving from there or cache depending on preferences.

As my company dedicated some time to give back to the community that we use so often time was limited on this article, hence the list of limitations. I hope that this technique, which is certainly useful, will help others in a similar predicament.

Thanks for your feedback. I'll be looking into why exceptions should be serializable. Initially I was throwing alot more exceptions but cut this down for practical purposes and I was close to deleting the exception class. Did you install the code? Was it easy to configure and use?