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

Posted by: Suprotim Agarwal , on 10/13/2008, in Category ASP.NET
Views: 53095
Abstract: We often create applications where we retrieve images from the database and display it on the page. At times, we may need to add some text or a copyright notice dynamically on the images. This article discusses how to do so using an ASP.NET Http Handler.
Dynamically Add Text to Images Retrieved from the Database using an ASP.NET Http Handler
 
We often create applications where we retrieve images from the database and display it on the page. At times, we may need to add some text or a copyright notice dynamically on the images. This article discusses how to do so using an ASP.NET Http Handler.
We will be using the data from the Employees table of the Northwind database since it contains images.
Note: I am using Visual Studio 2008 and I have targeted the application to use .NET 3.5
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
The entire source code of the article in C# and VB.NET can be downloaded from here. I hope you liked the article and I thank you for viewing it.
Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles
Suprotim Agarwal, ASP.NET Architecture MVP, MCSD, MCAD, MCDBA, MCSE, is the CEO of A2Z Knowledge Visuals Pvt. He primarily works as an Architect Consultant and provides consultancy on how to design and develop .NET centric database solutions.

Suprotim is the founder and primary contributor to DotNetCurry, DNC .NET Magazine, SQLServerCurry and DevCurry. He has also written an EBook 51 Recipes using jQuery with ASP.NET Controls. and is authoring another one at The Absolutely Awesome jQuery CookBook.

Follow him on twitter @suprotimagarwal


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by karu7310 on Saturday, December 27, 2008 3:14 AM
Good Job........
Comment posted by Tajudeen Iwalola on Wednesday, February 11, 2009 7:37 AM
Thanks for these beautiful codes. I have not try the code on my server. My question is how can you display other form fields like: first name, last name, date of birth, state of origing , sex, marital status and other customers details. I mean the information should be submitted to mssql or ms access that base, and before submission, the image and that has to be preview. On submission the image and data submitted should be displayed on the brower. I t would be highly appreciated if u can come up with these codes. i have gone theough the web i hv not seen such a codes....Tajudeen Iwalola mobile 2348056410758
Comment posted by manu on Tuesday, April 7, 2009 12:55 PM
i want to know how can we retrieve the embedded text from the image back is there ane method to do it?
Comment posted by guru on Friday, September 24, 2010 12:25 PM
Hi,

Thank you so much for the code. I made some changes so i can have 4 images on each other.

My question is my httphandler works fine on the localhost without even adding anything to the web.config file. But it doesnt work when I deployed the website? I am using iss7. Do I have to register this to the web.config file to make it work? if so how. Since this is my first handler. Also how would I install/map it to iis7.

Also I would have to create a .dll file to do it but I am not sure if i just compile the .ashx file it will just make it into a .dll file.


Thank you very much

Guru
Comment posted by SVK on Wednesday, May 4, 2011 6:55 AM
this code is extremely good & beneficial
i tried the same & tred some modification
i want even the text msg from the database
i tried to retirvive the msg from databse in
public void ProcessRequest(HttpContext context) event

but it was giving me error
and secondly i dont want the omage to be watermarked , & in the code it has a id called from aspx page , how do i naviagte thr; multiple  images & get differnet text stored in db

Comment posted by Mark Hipwell on Wednesday, April 11, 2012 8:57 AM
What is it with .NET developers? Why are you, as an MCDBA, accessing, or encouraging others to access, an SQL database using inline SQL? The only way anyone should ever access an SQL database is via stored procedures. Anyone reading this who does not know all the reasons why using inline SQL is bad practice has no place doing even basic static HTML web pages, let alone trying to teach others bad habits! Not impressed.
Comment posted by Suprotim Agarwal on Thursday, April 12, 2012 8:14 AM
@markhipwell - point taken. I do not want to go into the old Stored Proc vs Inline sql debate, but in scenarios like these, where a single line of code is needed, if I have to write one SP for every line, it can become a maintenance nightmare. The inline sql bad practice that you mention can be corrected with parameterized queries!

Having said that, I totally agree that SP's bring additional security and I personally prefer writing SP's in my apps. This scenario simply did not demand one!

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel