Abstract:
In this article we will see how we can change the email addresses written on a website to images on the fly. The general opinion about this task is to revamp the form where we are asking user to input the email address and then generate an image which will display later instead of email address.
Introduction:
In this article we will see how we can change the email addresses written on a website to images on the fly. The general opinion about this task is to revamp the form where we are asking user to input the email address and then generate an image which will display later instead of email address.
Create Email Filter:
Fortunately, Asp.net provides us with a feature called Response filter which allow us to alter the response generated by web server. By using Response filter we don’t need to alter the foundation of our application. We just need to plug that response filter in and our work is done.
So to create a filter we need to create a class called “EmailFilter.CS” which will inherit from Stream.
public class EmailFilter : Stream
{
private string _path;
private Stream _response;
public EmailFilter(Stream response, string path)
{
_response = response;
_path = path;
}
public override int Read(byte[] buffer, int offset, int count)
{
return _response.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
}
}
This can easily be read that we have two local variables which are getting set by our parameterized constructor and we have overrides two functions out of which the write function is really important. We are going to play with that function throughout the tutorial.
Note:
It is also important to mention here that we have removed some code for clarity but to use the stream class you need to override the some other functions and properties which you can find in code attached.
Now the next step is to write the code in Write method.
public override void Write(byte[] buffer, int offset, int count)
{
string html = System.Text.Encoding.UTF8.GetString(buffer);
string emailPattern = @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
Regex objRegex = new Regex(emailPattern);
MatchCollection objCol = objRegex.Matches(html);
foreach (Match m in objCol)
{
string email = m.Value;
Font objFont = new Font("Arial", 12, FontStyle.Bold);
int width = (int) getTextWidth(email, objFont);
Bitmap objBitmap = new Bitmap(width, 20);
Graphics objGraphics = Graphics.FromImage(objBitmap);
objGraphics.FillRectangle(Brushes.White, new Rectangle(0, 0, width, 20)); //getting white bg
objGraphics.DrawString(email, objFont, Brushes.Black, new Point(0, 0));
string relpath = "images/" + Guid.NewGuid().ToString() + ".jpg";
string saveFileName = _path + relpath;
objBitmap.Save(saveFileName,System.Drawing.Imaging.ImageFormat.Jpeg);
objGraphics.Dispose();
objBitmap.Dispose();
string imgMarkup = "<img src='" + relpath + "'/>";
html = html.Replace(email, imgMarkup);
}
buffer = System.Text.Encoding.UTF8.GetBytes(html);
_response.Write(buffer, offset, count);
}
protected float getTextWidth(string strText, Font f)
{
float width = 0;
Bitmap objBitmap = new Bitmap(1, 1);
Graphics objGraphics = Graphics.FromImage(objBitmap);
try
{
width = objGraphics.MeasureString(strText, f).Width;
return width;
}
catch (Exception)
{
return 0;
}
finally
{
objBitmap.Dispose();
objGraphics.Dispose();
}
}
The first four lines are easy to understand we are just storing the HTML generated by web server to a variable and we have created a regular expression object to check for the email addresses in out html.
In foreach loop we are generating the images of every email address using System.Drawing and saving the images in a temp location and finally disposing the objects.Now the main stuff is here, we are creating a string variable which will hold html image tag with source and then replace the email address with the image tag.
Whereas, getTextWidth is a simple function which measure the width of our text and return it so that we can have dynamic width for different size of texts.
Let's use this response filter in our aspx page. To do that we need to write the following code in Page_Load
protected void Page_Load(object sender, EventArgs e)
{
Response.Filter = new EmailFilter(Response.Filter, Server.MapPath("/").ToString());
}
It is worth to mention here the parameter. Our Response filter is just a class file and we donot have the Server path in classes but we still want to store the images using relative path so to do that we just pass the root path as parameter which we will use in Response Filter to save the image. To understand the situation correctly we need to write the following code in the form tag of our aspx.
<div>
aghausman@gmail.com </br>
aghausman@ciberstrategies.com</br>
me@aghausman.net</br>
</div>
That is nothing but three simple email addresses. Now when you run the page you will see the following output.
Notice that we have write three email addresses but it is showing only two. To see what went wrotn we have let's have a look at the source given below:
Notice that there is no html after the br of second image. It means there is some thing wrong with the response.write. Now to understand what happen we need to go back and read the last line of our Write function which is as follows
_response.Write(buffer, offset, count);
here we are simply passing what we have get from Write parameter but the matter of fact is that it is now changed. Remember, we have change our email address with the image tag so to compare let's see our first email address and frst generated image.
Email Address : aghausman@gmail.com
Image Text : <img src='images/da04a458-7ff4-41cb-b982-5724c1eadb84.jpg'/>
Notice that image tag is using more then double characters. so we need to change the last parameter count according to the new condition. for that let's see the Write method code again.
public override void Write(byte[] buffer, int offset, int count)
{
string html = System.Text.Encoding.UTF8.GetString(buffer);
string emailPattern = @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";
Regex objRegex = new Regex(emailPattern);
MatchCollection objCol = objRegex.Matches(html);
int totCount = 0;
foreach (Match m in objCol)
{
string email = m.Value;
Font objFont = new Font("Arial", 12, FontStyle.Bold);
int width = (int) getTextWidth(email, objFont);
Bitmap objBitmap = new Bitmap(width, 20);
Graphics objGraphics = Graphics.FromImage(objBitmap);
objGraphics.FillRectangle(Brushes.White, new Rectangle(0, 0, width, 20)); //getting white bg
objGraphics.DrawString(email, objFont, Brushes.Black, new Point(0, 0));
string relpath = "images/" + Guid.NewGuid().ToString() + ".jpg";
string saveFileName = _path + relpath;
objBitmap.Save(saveFileName,System.Drawing.Imaging.ImageFormat.Jpeg);
objGraphics.Dispose();
objBitmap.Dispose();
string imgMarkup = "<img src='" + relpath + "'/>";
totCount += imgMarkup.Length;
html = html.Replace(email, imgMarkup);
}
buffer = System.Text.Encoding.UTF8.GetBytes(html);
_response.Write(buffer, offset, count + totCount);
}
We have only create an int variable before loop and on every loop we are adding the length of image tag to the variable and finally on response.write we are adding the count with the length of image tags we have generated. that's it.
Now if we run the page we will see the following output:
Conclusion:
That's all, we have now replace the emal addresses written on our website to images. Just in case you don't want to write the page_load stuff on every page as it is very difficult then you can also use httpModule. For the see the following code
public class EmailToImages : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
HttpContext context = HttpContext.Current;
context.Response.Filter = new EmailFilter(context.Response.Filter, context.Server.MapPath("/"));
}
}
<httpModules>
<add name="EmailToImage" type="LearnWebApp.EmailToImages, LearnWebApp"/>
</httpModules>
Now to remove the temperory images you might need a code which delete the images in timely fashion. That's all.
[Download Sample]