Creating a Captcha control – Part:2


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:

Final Captcha image

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

Advertisements

12 thoughts on “Creating a Captcha control – Part:2

  1. 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

  2. Hi Aref

    I have put your captcha in a DnnNuke module. It works fine on my local developedment machine. However on the deployment machine, the picture does not show. Have a look at the website above. Any help?

    Khalid

  3. ykahn says:

    Dear Aref Karimi,

    First thanks for your captcha control.
    I have got a problem to catch out the captcha inside a RegisterUser wizard. You answered to that problem telling that we have to fetch the control insode the wizard. I am doing this but in fact it is impossible to cast properly the catcha control into the “MyCatcha” type. The control that I have is of type : “ASP.captcha_mycaptcha_ascx”.
    Could you know what problem it could be ?

    PS : I am using VS 2010

    Many thanks in advance

  4. Hi ASPGUY,

    Good article. I am trying to implement the CAPTCHA in SharePoint 2007 and having trouble. I don’t know exactly what to do with the ashx file. I can not get an image to show on the page. Any ideas?

    It would be great if you added a version of your write up for SharePoint.

    Guy

  5. Hi,

    I have downloaded the source files from Codeplex.com but not sure how to install and use the control. can you please send me a link or instructions on how to use this great control on a ASP Web page.

    I need idiot’s guid intryctions if you can please

    • Aref Karimi says:

      Hi Mike
      There is a sample web site included in the package. Just drop the control and read a couple of properties, as shown in the sample project.

  6. chilledflame says:

    Hi Aref,

    Thanks for the article, just a quick point taking this line of code as an example:

    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);

    }

    Move the random variable instatiation outside the loop and you can lose the thread.sleep.

    Each time you instatiate within the loop, random is reseeded with the current time and will result in less randomness 🙂

  7. chilledflame says:

    Hi Aref,

    One other thing, the codeplex page has a Pixelate procedure.

    Moving the line Image.FromFile(HttpContext…… outside the loop will make massive performance difference, as the file is only load once and not 200 times 🙂 i.e

    private void Pixelate(ref Bitmap bmp)
    {
    Random rnd = new Random();
    Image background = Image.FromFile(HttpContext.Current.Server.MapPath(“captcha.png”));
    Color[] Colors = { Color.Gray, Color.Red, Color.Blue, Color.Olive };
    for (int i = 0; i < 200; i++)
    {
    var grp = Graphics.FromImage(bmp);

    grp.DrawImage(background, new Rectangle(0, 0, bmp.Width, bmp.Height));
    }

    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s