Pages

Monday 4 February 2013

Dynamically Add Text to Images Retrieved from the Database using an ASP.NET Http Handler

Step 1: Open VS 2008. Click File > New > Website. Choose ASP.NET Website from the list of installed template, choose target platform as .NET Framework 3.5, choose the desired language and enter the location where you would like to store the website on your FileSystem. I have created a folder called VS2008 Projects, so the location over here is C:\VS2008 Projects\AddTextToImages. After typing the location, click OK.
Step 2: Since we are retrieving the images from the database, we would need a connection string. Add the following connection string to your web.config
      <connectionStrings>
            <add name="NorthwindConnectionString" connectionString="Data Source=(local);Initial Catalog=Northwind;Integrated Security=True" providerName="System.Data.SqlClient"/>
      </connectionStrings>
Step 3: We will be using an HttpHandler to pull images and then add some text to the retrieved images. I just love using handlers to handle scenarios like these as they provide me with a lot of flexibility while accessing server-side resources. To create an Http handler, right click project > Add New Item > Generic Handler > TextOnImageHandler.ashx. Add the following code to the handler.
C#
<%@ WebHandler Language="C#" Class="TextOnImageHandler" %>
 
using System;
using System.Web;
using System.Drawing;
using System.Configuration;
using System.ComponentModel;
using System.Drawing.Imaging;
using System.Data;
using System.Data.SqlClient;
 
public class TextOnImageHandler : IHttpHandler {
 
    byte[] empPic = null;
    long seq = 0;
 
    public void ProcessRequest(HttpContext context)
    {
        Int32 empno;
 
        if (context.Request.QueryString["id"] != null)
            empno = Convert.ToInt32(context.Request.QueryString["id"]);
        else
            throw new ArgumentException("No parameter specified");
 
        // Convert Byte[] to Bitmap
        Bitmap newBmp = ConvertToBitmap(ShowEmpImage(empno));
        // Watermark Text to be added to image
        string text = "Code from dotnetcurry";
        if(newBmp != null)
        {
           Bitmap convBmp = AddTextToImage(newBmp, text);
           convBmp.Save(context.Response.OutputStream,  ImageFormat.Jpeg);
           convBmp.Dispose();
        }
        newBmp.Dispose();
 
    }
 
    // Add Watermark Text to Image
    protected Bitmap AddTextToImage(Bitmap bImg, string msg)
    {
        // To void the error due to Indexed Pixel Format
        Image img = new Bitmap(bImg, new Size(bImg.Width, bImg.Height));
        Bitmap tmp = new Bitmap(img);
        Graphics graphic = Graphics.FromImage(tmp);
        // Watermark effect
        SolidBrush brush = new SolidBrush(Color.FromArgb(120, 255, 255, 255));
        // Draw the text string to the Graphics object at a given position.
        graphic.DrawString(msg, new Font("Times New Roman", 14, FontStyle.Italic),
             brush, new PointF(10, 30));
        graphic.Dispose();
        return tmp;
    }
 
    // Convert byte array to Bitmap (byte[] to Bitmap)
    protected Bitmap ConvertToBitmap(byte[] bmp)
    {
        if(bmp != null)
        {
        TypeConverter tc = TypeDescriptor.GetConverter(typeof(Bitmap));
        Bitmap b = (Bitmap)tc.ConvertFrom(bmp);
        return b;
        }
    
    }
 
    public byte[] ShowEmpImage(int empno)
    {
        string conn = ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString;
        SqlConnection connection = new SqlConnection(conn);
        string sql = "SELECT photo FROM Employees WHERE EmployeeID = @ID";
        SqlCommand cmd = new SqlCommand(sql, connection);
        cmd.CommandType = CommandType.Text;
        cmd.Parameters.AddWithValue("@ID", empno);
        connection.Open();
        SqlDataReader dr = cmd.ExecuteReader();
        if (dr.Read())
        {
            seq = dr.GetBytes(0, 0, null, 0, int.MaxValue) - 1;
            empPic = new byte[seq + 1];
            dr.GetBytes(0, 0, empPic, 0, Convert.ToInt32(seq));
            connection.Close();
        }
 
        return empPic;
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
VB.NET
<%@ WebHandler Language="vb" Class="TextOnImageHandler" %>
 
Imports System
Imports System.Web
Imports System.Drawing
Imports System.Configuration
Imports System.ComponentModel
Imports System.Drawing.Imaging
Imports System.Data
Imports System.Data.SqlClient
 
Public Class TextOnImageHandler
      Implements IHttpHandler
 
      Private empPic() As Byte = Nothing
      Private seq As Long = 0
 
      Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
            Dim empno As Int32
 
            If context.Request.QueryString("id") IsNot Nothing Then
                  empno = Convert.ToInt32(context.Request.QueryString("id"))
            Else
                  Throw New ArgumentException("No parameter specified")
            End If
 
            ' Convert Byte[] to Bitmap
            Dim newBmp As Bitmap = ConvertToBitmap(ShowEmpImage(empno))
            ' Watermark Text to be added to image
            Dim text As String = "Code from dotnetcurry"
            Dim convBmp As Bitmap = AddTextToImage(newBmp, text)
            convBmp.Save(context.Response.OutputStream, ImageFormat.Jpeg)
            newBmp.Dispose()
            convBmp.Dispose()
      End Sub
 
      ' Add Watermark Text to Image
      Protected Function AddTextToImage(ByVal bImg As Bitmap, ByVal msg As String) As Bitmap
            ' To void the error due to Indexed Pixel Format
            Dim img As Image = New Bitmap(bImg, New Size(bImg.Width, bImg.Height))
            Dim tmp As New Bitmap(img)
            Dim graphic As Graphics = Graphics.FromImage(tmp)
            ' Watermark effect
            Dim brush As New SolidBrush(Color.FromArgb(120, 255, 255, 255))
            ' Draw the text string to the Graphics object at a given position.
            graphic.DrawString(msg, New Font("Times New Roman", 14, FontStyle.Italic), brush, New PointF(10, 30))
            graphic.Dispose()
            Return tmp
      End Function
 
      ' Convert byte array to Bitmap (byte[] to Bitmap)
      Protected Function ConvertToBitmap(ByVal bmp() As Byte) As Bitmap
         If bmp IsNot Nothing Then 
            Dim tc As TypeConverter = TypeDescriptor.GetConverter(GetType(Bitmap))
            Dim b As Bitmap = CType(tc.ConvertFrom(bmp), Bitmap)
            Return b
         End If
         Return Nothing
      End Function
 
      Public Function ShowEmpImage(ByVal empno As Integer) As Byte()
            Dim conn As String = ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString
            Dim connection As New SqlConnection(conn)
            Dim sql As String = "SELECT photo FROM Employees WHERE EmployeeID = @ID"
            Dim cmd As New SqlCommand(sql, connection)
            cmd.CommandType = CommandType.Text
            cmd.Parameters.AddWithValue("@ID", empno)
            connection.Open()
            Dim dr As SqlDataReader = cmd.ExecuteReader()
            If dr.Read() Then
                  seq = dr.GetBytes(0, 0, Nothing, 0, Integer.MaxValue) - 1
                  empPic = New Byte(seq){}
                  dr.GetBytes(0, 0, empPic, 0, Convert.ToInt32(seq))
                  connection.Close()
            End If
 
            Return empPic
      End Function
 
      Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
            Get
                  Return False
            End Get
      End Property
End Class
The steps for adding some text to the image, using the handler are as follows:
1.    The EmployeeID whose image is to be retrieved, is passed to the handler via query string. We use the Request.QueryString[“id”] to retrieve the EmployeeID(emp_id) from the handler url. The ID is then passed to the ‘ShowEmpImage()’ method where the image is fetched from the database using SqlDataReader and returned in a byte[] object.
2.    We pass this byte[] to the ConvertToBitmap() function where we use the TypeConverter class to convert a byte array to bitmap.
3.    We then pass the bitmap and the text to be written on the Bitmap, to the AddTextToImage() method. If the image has an indexed pixel format, then directly calling the method Graphics.ShowImage() would throw an exception with the message, "A Graphics object cannot be created from an image that has an indexed pixel format”. A workaround is to create a temporary bitmap with the same width and height as that of the original bitmap and then create a new Graphics object from the Image.
4.    We then use the Graphics.DrawString() to draw the specified string at the specified location. The watermark effect is achieved using an alpha component of 120 provided in the Color.FromArgb() method.
5.    The last step is to save the image to the page's output stream and indicate the image format as shown here convBmp.Save(context.Response.OutputStream, ImageFormat.Jpeg)
You can call this handler in the following way:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Write Text On Image</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
         <img alt="Image" id="img1" src="TextOnImageHandler.ashx?id=2" />
    </div>
     </form>
</body>
</html>
The image with the watermark text will look similar to the following (Notice the watermark text 'Code from dotnetcurry'):
Text Image Handler

No comments:

Post a Comment