March 22, 2011

In the previous articles, I showed how to create the captcha control. In this post, I will write about the handler that generates the image based on the dynamic random text generated by the GetRandomText method of the CaptchaControl usercontrol.

To generate the image, the handler needs to send its output as an image type. To get random text, it needs to access the value from the Session. The handler uses the System.Drawing.Bitmap class to create the image. The width of the image is dynamically based on the number of characters of the random text. The handler uses the background image path property to set the background image and uses the font colour, font size and font family to write the text on the image. The RotateTransform method of the System.Drawing.Graphics class is used to slightly rotate the text on the image.  the yPos method is used to randomly position the characters. The output image is then send through the stream.

The code for the class is below and can also be downloaded from CodePlex at http://captchadotnet.codeplex.com/ .

CaptchaHandler
  1. using System;
  2. using System.Web;
  3. using System.Drawing;
  4.  
  5. namespace CaptchaApp
  6. {
  7.     /// <summary>
  8.     /// Summary description for CaptchaHandler
  9.     /// </summary>
  10.     public class CaptchaHandler : IHttpHandler, System.Web.SessionState.IReadOnlySessionState
  11.     {
  12.  
  13.         private Captcha cc;
  14.  
  15.         public void ProcessRequest(HttpContext context)
  16.         {
  17.             context.Response.ContentType = "image/jpeg";
  18.  
  19.             if (context.Session != null)
  20.             {
  21.                 if (context.Session["CaptchaClass"] != null)
  22.                 {
  23.                     cc = (Captcha)context.Session["CaptchaClass"];
  24.                 }
  25.                 else
  26.                 {
  27.                     cc = new Captcha();
  28.                 }
  29.             }           
  30.  
  31.             string text = string.Empty;
  32.             if(context.Session["captcha"] !=null)
  33.                 text = (string)context.Session["captcha"];
  34.  
  35.             int imageWidth = Convert.ToInt32(((cc.FontSize + 8) * text.Length));
  36.             int imageHeight = Convert.ToInt32(cc.FontSize * 2.5);
  37.  
  38.  
  39.             Bitmap bmp = new Bitmap(imageWidth, imageHeight);
  40.             var Grph = Graphics.FromImage(bmp);
  41.             Grph.FillRectangle(new SolidBrush(Color.Lavender), 0, 0, bmp.Width, bmp.Height);
  42.  
  43.             var grp = Graphics.FromImage(bmp);
  44.             Image background = Image.FromFile(HttpContext.Current.Server.MapPath(cc.BackgroundImagePath));
  45.             grp.DrawImage(background, new Rectangle(0, 0, bmp.Width, bmp.Height));
  46.  
  47.  
  48.             Grph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
  49.             Grph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  50.             int xPos = 10;
  51.  
  52.             Font f = cc.GetFont();
  53.  
  54.             char[] textArray = text.ToCharArray();
  55.             int yPosition = 0;
  56.             Random r = new Random();
  57.  
  58.             for (int i = 0; i < textArray.Length; i++)
  59.             {
  60.                 if (i % 2 == 0)
  61.                     Grph.RotateTransform(5);
  62.                 else
  63.                     Grph.RotateTransform(-5);
  64.  
  65.                 yPosition = (int)(r.NextDouble() * 10);
  66.  
  67.                 Grph.DrawString(textArray[i].ToString(), f, new SolidBrush(Color.FromName(cc.TextColor)), xPos, yPosition);
  68.                 xPos += 20;
  69.  
  70.             }
  71.             
  72.             bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
  73.  
  74.         }
  75.  
  76.         public bool IsReusable
  77.         {
  78.             get
  79.             {
  80.                 return false;
  81.             }
  82.         }
  83.  
  84.  
  85.         private int yPos()
  86.         {
  87.             Random r = new Random();
  88.             return (int)(r.NextDouble() * 10);
  89.         }                
  90.     }
  91. }

The handler also implements the System.Web.SessionState.IReadOnlySessionState interface beside the IHttpHandler. This is necessary so that the handler can access the Session of the current context.

Creating a Captcha Control – Part 4

In one of the previous posts – Creating a Captcha Control – Part 2 – I was talking about Captcha class that contains some of the properties of the control like text colour, font size, font family and background image path. The class basically defines the setter and the getter for few of the properties. The code for the class is below.

Captcha Class
  1. using System;
  2.  
  3. namespace CaptchaApp
  4. {
  5.     public class Captcha
  6.     {
  7.  
  8.  
  9.         public string FontFamily
  10.         {
  11.             get { return fontFamily; }
  12.             set
  13.             {
  14.                 if (value != string.Empty && value != null)
  15.                     this.fontFamily = value;
  16.                 else
  17.                     this.fontFamily = "Arial";
  18.             }
  19.         }
  20.  
  21.         public double FontSize
  22.         {
  23.             get { return fontSize; }
  24.             set
  25.             {
  26.                 try
  27.                 {
  28.                     if (value <= 10 || value >= 24)
  29.                         this.fontSize = 16;
  30.                     else
  31.                         this.fontSize = value;
  32.                 }
  33.                 catch (Exception ex)
  34.                 {
  35.                     this.fontSize = 16;
  36.                 }
  37.             }
  38.         }
  39.  
  40.         public string TextColor
  41.         {
  42.             get { return textColor; }
  43.             set
  44.             {
  45.                 if (value == string.Empty || value == null)
  46.                     this.textColor = "Black";
  47.                 else
  48.                     this.textColor = value;
  49.             }
  50.         }
  51.  
  52.         public string BackgroundImagePath
  53.         {
  54.             get { return backgroundImagePath; }
  55.             set
  56.             {
  57.                 this.backgroundImagePath = value;
  58.             }
  59.         }
  60.  
  61.         public System.Drawing.Font GetFont()
  62.         {
  63.             return new System.Drawing.Font(FontFamily, (float)FontSize);
  64.         }
  65.  
  66.  
  67.         private double fontSize;
  68.         private string fontFamily;
  69.         private string backgroundImagePath;
  70.         private string textColor;
  71.     }
  72.  
  73.  
  74. }

The full code for the control can be downloaded from CodePlex at http://captchadotnet.codeplex.com/

March 16, 2011

Creating a Captcha Control – Part 3

Adding Audio

In my previous 2 articles – Creating a Captcha Control – Part 1 and Creating a Captcha Control – Part 2 – I have created the user control for the captcha control. The control also allows functionality to play audio to read the text on the captcha.

To achieve the audio feature, I have used a COM library – Microsoft Speech Object Library. To add the COM library, I added a reference to Microsoft Speech Object Library (%Windir%\System32\Speech\Common\Sapi.dll). To add a reference, right-click on the Reference folder, click Add Reference, click COM tab and add the library.

captcha-add-reference

Once the reference is added, the SpeechLib namespace is imported as shown below.

Adding reference to SpeechLib
  1. using SpeechLib;

Then, the Speak method in SpVoice class is used to play the audio as per below.

ReadCaptcha method
  1. protected void ReadCaptcha(object sender, EventArgs e)
  2. {
  3.     SpVoice voice = new SpVoice();
  4.  
  5.     char[] text = ((string)ViewState["captcha"]).ToCharArray();
  6.     
  7.  
  8.     for (int i = 0; i < text.Length; i++)
  9.     {
  10.         voice.Speak(text[i].ToString(), SpeechVoiceSpeakFlags.SVSFDefault);
  11.     }
  12. }

As can be seen, I am reading out each of the text and not all the text at once. The reason is, SpVoice class will try to read it as a word instead of individual characters otherwise.

In the following article, I will show how to create and use the handler to generate the image. The whole code for the captcha control can be downloaded from CodePlex at http://captchadotnet.codeplex.com/

In my last article – Creating a Captcha Control – Part 1, I discussed on the front end of the user control that creates the captcha. In this article, I will discuss on the code-behind of the control – that is, what properties are set and how and what methods are used.

The following code shows the properties that are used.

User Control - Properties
  1. #region field initialisation
  2.  
  3. public Captcha cc;
  4. private int captchaLength;
  5. private double fontSize;
  6. private string fontFamily;
  7. private string backgroundImagePath;
  8. private string textColor;
  9. private string successMessage;
  10. private string errorMessage;
  11. private string characterSet;
  12.  
  13. #endregion

Then, the setters and getters for these properties are created as per below.

User Control - Setter / Getter
  1. #region GetterSetter
  2. public string TestButtonText
  3. {
  4.     get { return B1.Text; }
  5.     set { B1.Text = value; }
  6. }
  7.  
  8. public string SpeakButtonText
  9. {
  10.     get { return BSpeak.Text; }
  11.     set { BSpeak.Text = value; }
  12. }
  13.  
  14. public string ReLoadButtonText
  15. {
  16.     get { return B2.Text; }
  17.     set { B2.Text = value; }
  18. }
  19.  
  20. public string SuccessMessage
  21. {
  22.     get { return successMessage; }
  23.     set { successMessage = value; }
  24. }
  25.  
  26. public string ErrorMessage
  27. {
  28.     get { return errorMessage; }
  29.     set { errorMessage = value; }
  30. }
  31.  
  32. public int CaptchaLength
  33. {
  34.     get { return captchaLength; }
  35.     set
  36.     {
  37.         try
  38.         {
  39.             int k = Convert.ToInt32(value);
  40.             if (k < 5 || k > 10)
  41.                 captchaLength = 6;
  42.             else
  43.                 captchaLength = k;
  44.         }
  45.         catch (Exception ex)
  46.         {
  47.             captchaLength = 6;
  48.         }
  49.     }
  50. }
  51.  
  52. public string FontFamily
  53. {
  54.     get { return fontFamily; }
  55.     set
  56.     {
  57.         if (value != string.Empty && value != null)
  58.             fontFamily = value;
  59.         else
  60.             fontFamily = "Arial";
  61.     }
  62. }
  63.  
  64. public double FontSize
  65. {
  66.     get { return fontSize; }
  67.     set
  68.     {
  69.         try
  70.         {
  71.             fontSize = Convert.ToInt32(value);
  72.             if (fontSize <= 10 && fontSize >= 24)
  73.                 fontSize = 16;
  74.         }
  75.         catch (Exception ex)
  76.         {
  77.             fontSize = 16;
  78.         }
  79.     }
  80. }
  81.  
  82. public string BackgroundImagePath
  83. {
  84.     get { return backgroundImagePath; }
  85.     set
  86.     {
  87.         if (System.IO.File.Exists(Server.MapPath(value)))
  88.             backgroundImagePath = value;
  89.         else
  90.             backgroundImagePath = System.Configuration.ConfigurationManager.AppSettings["defaultImagePath"];
  91.     }
  92. }
  93.  
  94. public string TextColor
  95. {
  96.     get { return textColor; }
  97.     set
  98.     {
  99.         if (value == string.Empty || value == null)
  100.             textColor = "Black";
  101.         else
  102.             textColor = value;
  103.     }
  104. }
  105.  
  106. public string CharacterSet
  107. {
  108.     get { return characterSet; }
  109.     set
  110.     {
  111.         if (value == "" || value == null)
  112.             characterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
  113.         else
  114.             characterSet = value;
  115.     }
  116. }
  117.  
  118. #endregion

The setters and getters are quite straight forward. The captcha length property defaults to 6 in case the value entered is not a number and if the value is less than 6 or greater than 10.

The default font family is set to Arial.

The default font size is set to 16. If fonts are too small – less than 10px – or too large – greater than 24px – the font is automatically set to 16.

The default colour is set to black.

The default character set consists of all letters in uppercase and number from 1 to 9. I have not included lower case letters and 0 as these are sometimes confusing.

The default background image is set within the web.config file. Note that the background image is expected to be within the solution and would require “~” notation to work properly.

Now, in the Page_Load event shown below, I am setting the default values and loading the captcha.

Page_Load event
  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3.  
  4.     B1.Text = TestButtonText;
  5.     B2.Text = ReLoadButtonText;
  6.     BSpeak.Text = SpeakButtonText;
  7.     
  8.     SetValues();
  9.  
  10.     cc = GetCaptchaClass();
  11.  
  12.     if (!IsPostBack)
  13.     {
  14.         LoadCaptcha();
  15.     }
  16. }

As can be seen, the Button’s text are set a SetValues method is called which sets values for other properties and the method – LoadCaptcha is called if it is not a postback. Another class Captcha is set to method GetCaptchaClass which initiates a Captcha object. I will talk about the Captcha object in a little while.

The SetValues method is below.

SetValues method
  1. private void SetValues()
  2. {
  3.     if (CharacterSet == null)
  4.         CharacterSet = "";
  5.     if(CaptchaLength == 0)
  6.         CaptchaLength = 6;
  7.  
  8.     if(BackgroundImagePath == null)
  9.         BackgroundImagePath = "";
  10.     
  11.     if(FontFamily == null)
  12.         FontFamily = "";            
  13.     
  14.     if(FontSize == 0)
  15.         FontSize = 0.0;
  16.  
  17.     if(TextColor == null)
  18.         TextColor = "";
  19. }

The LoadCaptcha method is interesting – it creates random text and adds it ViewState and Session, adds the Captcha object to Session and sets the path of the Image control to CaptchaHandler.ashx handler. The code for this method is below.

LoadCaptcha method
  1. private void LoadCaptcha()
  2. {
  3.     string text = GetRandomText();
  4.     
  5.     ViewState.Add("captcha", text);
  6.     Session.Add("CaptchaClass", cc);//add captcha object to Session
  7.     Session.Add("captcha", text);//add captcha text to session
  8.     Im1.ImageUrl = "CaptchaHandler.ashx";
  9. }

The random text for the captcha is generated by GetRandomText method which basically creates random text based on the CharacterSet property. The code for this method is below.

GetRandomText method
  1. private string GetRandomText()
  2. {
  3.     char[] letters = CharacterSet.ToCharArray();
  4.     string text = string.Empty;
  5.     Random r = new Random();
  6.     int num = -1;
  7.  
  8.     for (int i = 0; i < this.CaptchaLength; i++)
  9.     {
  10.         num = (int)(r.NextDouble() * (letters.Length - 1));
  11.         text += letters[num].ToString();
  12.     }
  13.     return text;
  14. }

The Captcha class basically properties for the captcha text. The GetCaptchaClass method initiates the Captcha class and set its properties. The code for this method is below.

GetCaptchaClass method
  1. private Captcha GetCaptchaClass()
  2. {
  3.     if (Session["CaptchaClass"] != null)
  4.         cc = (Captcha)Session["CaptchaClass"];
  5.     else
  6.         cc = new Captcha();
  7.  
  8.     cc.FontSize = this.FontSize;
  9.     cc.FontFamily = this.FontFamily;
  10.     cc.BackgroundImagePath = this.BackgroundImagePath;
  11.     cc.TextColor = this.TextColor;
  12.     return cc;
  13. }

That’s pretty much all the methods for the user control. When the Reload Button is clicked the LoadAnother method is run that simply calls the LoadCaptcha method as per below.

LoadAnother method
  1. protected void LoadAnother(object sender, EventArgs e)
  2. {
  3.     LoadCaptcha();
  4. }

When the Test button is clicked, the ValidateCaptcha method is run that compares the text entered with the text in the ViewState. The code is below.

ValidateCaptcha method
  1. protected void ValidateCaptcha(object sender, EventArgs e)
  2. {
  3.     string text = T1.Text;
  4.     if (text == (string)ViewState["captcha"])
  5.         LStatus.Text = SuccessMessage;
  6.     else
  7.         LStatus.Text = ErrorMessage;
  8. }

In my next article, I will explain how the reading out the captcha text works. The captcha control is also available for download from CodePlex at http://captchadotnet.codeplex.com/

The word CAPTCHA means “Completely Automated Public Turing to tell Computers and Humans Apart”.Captcha is commonly used in registration forms and other forms to disallow spiders and / or bots to automatically submit a form. Captcha is usually some text on an image that can be read by humans but unreadable by computers. An example image of a captcha is below.

Captcha 

Creating a captcha controls involves dynamically generating some text, converting the text into an image, passing the image to the client, pass on the text in some other format so it could be tested against. In this particular instance, I have created a captcha control through which we control properties like font size, colour, background image, character set to choose the characters from and number of characters to display. It will also control additional properties for setting text for various buttons like Test button, Reload button and success and error message. The control also contains option for reading out the text for accessibility reasons.

The control could be used like below:

Using the control
  1. <uc1:CaptchaControl ID="CaptchaControl1" runat="server" TestButtonText="Try" BackgroundImagePath="~/images/captcha2.png"
  2. ErrorMessage="Try Another Image" SuccessMessage="good job" CaptchaLength="10" CharacterSet="ABCDEFG123456"
  3. ReLoadButtonText="Reload" FontSize="20" FontFamily="Trebuchet MS" TextColor="Blue" />

After a new project is setup in Visual Studio, a user control is added to the project. The user control simply contains an Image control for displaying captcha, a TextBox control where a user would enter text for comparison, a Label control that displays status – correct or incorrect text entered, three Button controls – one for testing the user, one for reloading the captcha text in case the text cannot be read by the user and another button for reading out the captcha text. It’s important that the text could be read out as it helps visually impaired users to enter the information.

Code for the user control is below.

User Control - aspx
  1. <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CaptchaControl.ascx.cs" Inherits="CaptchaApp.CaptchaControl" %>
  2. <asp:Image ID="Im1" runat="server" />
  3. <asp:TextBox ID="T1" runat="server"></asp:TextBox>
  4. <asp:Button ID="B1" runat="server" Text="Test" OnClick="ValidateCaptcha" />
  5. <asp:Label ID="LStatus" runat="server"></asp:Label><br /><br />
  6. <asp:Button ID="B2" runat="server" Text="Load Another Image" OnClick="LoadAnother" />
  7. <asp:Button ID="BSpeak" runat="server" Text="Speak" OnClick="ReadCaptcha" />

As can be seen, the ImageUrl property for the Image control is not set. It will be set from code-behind.

In my next post, I will discuss how I have created the various properties of the control through the code-behind of this user control.

The source code of the captcha control can be downloaded from CodePlex  at http://captchadotnet.codeplex.com/

Reference: Shahed Kazi at AspNetify.com