In this post I will explain how to generate a hard-to-read image out of our Captcha text. The new Captcha with image will look like this:

In MyCaptcha control, each letter is different from other ones in three properties:
1- Font
2- Size
3- Distance from the next letter (character spacing)
Therefore, I wrote a class named Letter that each instance of it holds a character along with all its properties like it’s font name and size. The class has a constructor that accepts a character argument and assigns random properties to it:
public class Letter
{
string[] ValidFonts = {“Segoe Script”, “Century”, “Eccentric Std”,“Freestyle Script”,“Viner Hand ITC”};
public Letter(char c)
{
Random rnd = new Random();
font = new Font(ValidFonts[rnd.Next(ValidFonts.Count()-1)], rnd.Next(20)+20, GraphicsUnit.Pixel);
letter = c;
}
public Font font
{
get;
private set;
}
public Size LetterSize
{
get
{
var Bmp = new Bitmap(1, 1);
var Grph = Graphics.FromImage(Bmp);
return Grph.MeasureString(letter.ToString(), font).ToSize();
}
}
public char letter
{
get;
private set;
}
public int space
{
get;
set;
}
}
As you see in the above source code, I pick a random font name from ValidFonts array. The font names in ValidFonts array are Windows Vista fonts. You must keep in mind that you use font names that exist on your Web Server. I also recommend you to use fantasy fonts (like gothic) to make the produced image more hard-to-read.
I also have added a property of type Size to get the width and height of the letter when it is rendered with its own font. To get the character size I use Graphics.MeasureString method.
The ’space’ property is set when the Captcha text is being rendered. To render the captcha image, I use a generic handler (.ashx) file. Using a generic handler we can render any output type and send it to the output stream. An .ashx file can be treated like an .aspx file but has noticeably fewer overhead. For example we can pass query strings to it and generate the output based on it.
I will send the captcha text as a query string called CaptchaText to GetImgText.ashx generic handler. In the .ashx code, I will make an instance of Letter class for each character.
var CaptchaText = context.Request.QueryString["CaptchaText"];
if (CaptchaText != null)
{
List<Letter> letter = new List<Letter>();
int TotalWidth = 0;
int MaxHeight = 0;
foreach (char c in CaptchaText)
{
var ltr = new Letter(c);
int space = (new Random()).Next(5) + 1;
ltr.space = space;
letter.Add(ltr);
TotalWidth += ltr.LetterSize.Width+space;
if (MaxHeight < ltr.LetterSize.Height)
MaxHeight = ltr.LetterSize.Height;
System.Threading.Thread.Sleep(1);
}
As the above piece of code shows, all Letter instances are stored in letter generic list. The width of each letter plus its distance with the next letter is summarized in TotalWidth local variable. We also get the height of the biggest letter so that we get sure all letters fit in the captcha image.
I also have two constants for vertical and horizontal margins:
const int HMargin = 5;
const int VMargin = 3;
Thus, our image will have a size of VMargin+MaxHeight and HMargin+TotalWidth:
Bitmap bmp = new Bitmap(TotalWidth + HMargin, MaxHeight + VMargin);
var Grph = Graphics.FromImage(bmp);
At next step, I will draw each letter with it’s own font size and position it according to it’s space property:
int xPos = HMargin;
foreach (var ltr in letter)
{
Grph.DrawString(ltr.letter.ToString(), ltr.font, new SolidBrush(Color.Navy), xPos, VMargin );
xPos += ltr.LetterSize.Width + ltr.space;
}
Now the image is cooked and ready! We should send it to the output stream:
bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
Up to now the Captcha text is sketched well but I’d like to smudge it a little more in order to make it more hard-to-read. To do is I will draw a number of circles on random positions of the image. One nice idea is to define an IShape interface with a method named Sketch(). Then define different classes that implements IShape and draws different shapes on the image.
By the way, the code that smudges the image is this:
Color[] Colors = { Color.Gray , Color.Red, Color.Blue, Color.Olive };
for (int i = 0; i < 200; i++)
{
var rnd = new Random();
var grp = Graphics.FromImage(bmp);
grp.DrawEllipse(new Pen(Colors[rnd.Next(3)]), rnd.Next(bmp.Width – 1), rnd.Next(bmp.Height – 1), 5, 5);
System.Threading.Thread.Sleep(1);
}
You can download the source code of this control from HERE. The password of archived file is: aspguy.wordpress.com
There is also a project for this control at CodePlex. You can upload any changes you might make on the source code.
Have fun with Captchaing!
Aref Karmi
9 Apr 2009
HELLO ASPGUY
I GOT THE LINK TO YOUR BLOG FROM JOE – AFTER WATCHING HIS VIDEO
I AM TRYING YOUR CODE IN THE DESIGN FORM AND IT WORKS IN VISUAL WEB DESIGN 2008 PERFECT. MANY THANKS FOR IT –
TRIED IN EXPRESSION WEB AND HAD TROUBLE. JUST THAT I AM HALF WAY INTO DESIGNING MY CONTACT PAGE LAYOUT IN EXPRESSION WEB -
DO YOU HAVE ANY THOUGHTS HERE IN GETTING IT WORKING IN EXPRESSION WEB
Hie,
I sent you an email to your direct email address.