Virtual Developer Workshop: Containerized Development with Docker
Here's the scenario, you have a client that runs banner ads on their website. The client now wants to customize their banner ads when a user is logged in. That is, when Bob comes and logs in to the website they want the top of the banner ad to read "Hey Bob!". Now, you could obviously achieve the same results by simply having text over a background image in a table cell. However, the font that your client uses is very unusual and they want to be sure that the text on the image is the same font as they use throughout the rest of their website images.
The Project Code
The login form and code:
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="ImageOnTheFlyExample.aspx.vb" Inherits="QuinStreetSampleScripts.ImageOnTheFlyExample" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Images on the Fly Example</title> </head> <body> <form id="form1" runat="server"> <div> <table border="1" cellpadding="5" cellspacing="0" style="width: 500px"> <tr> <td colspan="2" style="font-size: 18pt; color: yellow; font-family: 'Baskerville Old Face'; background-color: maroon"> Images on the Fly Example</td> </tr> <tr> <td valign="top"> <strong>User Login<br /> </strong> <br /> Name: <asp:TextBox ID="TextBoxUserName" runat="server" Width="300px"></asp:TextBox><br /> <br /> <asp:Button ID="ButtonLogin" runat="server" Text="Login" /><br /> </td> <td valign="top"> <asp:Image ID="ImageCustomAd" runat="server" AlternateText="Images on the Fly - Example Ad" ToolTip="Images on the Fly - Example Ad" ImageUrl="~/images/BannerAdExample03242010.jpg" /></td> </tr> </table> </div> </form> </body> </html> ImageOnTheFlyExample.aspx.vb Partial Public Class ImageOnTheFlyExample Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load End Sub Protected Sub ButtonLogin_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonLogin.Click Me.ImageCustomAd.ImageUrl = "~/ImageOnTheFlyOutput.aspx?text=" & HttpUtility.UrlEncode(Me.TextBoxUserName.Text.Trim) End Sub End Class
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="ImageOnTheFlyOutput.aspx.vb" Inherits="QuinStreetSampleScripts.ImageOnTheFlyOutput" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> </form> </body> </html> ImageOnTheFlyOutput.aspx.vb Imports System.IO Imports System.Drawing Imports System.Drawing.Imaging Imports System.Drawing.Text Imports System.Drawing.Drawing2D Partial Public Class ImageOnTheFlyOutput Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load ' Get text to be displayed from querystring Dim MyUsernameText As String = "Hey, " & Request.QueryString("text") & "!" ' Declare image and output variables Dim MyBitmap As Bitmap Dim MyMemoryStream As New MemoryStream Dim MyGraphics As Graphics Dim MyGraphicsPath As New GraphicsPath Dim MyBrush As SolidBrush = New SolidBrush(Color.White) ' You can also use custom RGB colors Dim ContentStartPoint As New System.Drawing.Point(5, 5) Dim ContentWorkArea As New System.Drawing.Size(90, 30) ' Space where drawing will occur - (width, height) Dim MyImageRectangle As Rectangle = New Rectangle(ContentStartPoint, ContentWorkArea) ' Font variable - defines font family, font size, font style and font size type Dim MyFont As Font = New Font("Baskerville Old Face", _ 14, _ System.Drawing.FontStyle.Regular, _ System.Drawing.GraphicsUnit.Point) ' Set formatting Dim MyFormat As StringFormat = New StringFormat MyFormat.Alignment = StringAlignment.Center MyFormat.LineAlignment = StringAlignment.Center ' Get base image Dim MyImage As Image = Image.FromFile(Server.MapPath("images/BannerAdExample03242010.jpg")) MyBitmap = New Bitmap(MyImage) ' For quality purposes use a bitmap as the base image ' Set resoltion MyBitmap.SetResolution(72, 72) ' 72x72 is an average resolution for web ' Build the image ' Set Graphics MyGraphics = Graphics.FromImage(MyBitmap) ' Set graphics quality With MyGraphics .InterpolationMode = InterpolationMode.HighQualityBicubic .CompositingMode = CompositingMode.SourceOver .CompositingQuality = CompositingQuality.HighQuality .TextRenderingHint = TextRenderingHint.AntiAlias .SmoothingMode = SmoothingMode.AntiAlias End With ' Add text and font/style definitions to GraphicsPath which is used in FillPath to actually create the text MyGraphicsPath.AddString(MyUsernameText, MyFont.FontFamily, MyFont.Style, MyFont.Size, MyImageRectangle, MyFormat) MyGraphics.FillPath(MyBrush, MyGraphicsPath) ' Clear and set response Response.Clear() Response.ContentType = "image/png" ' Note: this must match the ImageFormat type you choose below ' Use memory stream to output page as image ' Use PNG format for best quality output MyBitmap.Save(MyMemoryStream, ImageFormat.Png) MyMemoryStream.WriteTo(Response.OutputStream) ' Clean up MyBitmap = Nothing MyImage = Nothing MyGraphics.Dispose() MyGraphicsPath.Dispose() MyBrush.Dispose() MyFont.Dispose() End Sub End ClassHow it Works
There are two pieces to this project. First, there is the simple pseudo login form. This does nothing except provide a textbox, login button and our ad image. It's only function is to take whatever username is entered into the textbox and use that to generate our custom ad image. This is accomplished by setting the
ImageUrl property to the page that renders our image with a query string containing the text entered in the textbox.
The interesting part of the project is the second piece which renders the custom image. In short, the page receives text to insert into our image from the querystring and builds the image from the base ad image, and then renders it via a
Of course, it's not as simple as 1, 2, 3, so we'll break down the code for
ImageOnTheFlyOutput.aspx. The first thing you should notice is that the project uses
System.IO and several
System.Drawing classes. You will also see everything happens within the Page Load event. The page has no controls, it simply renders an image, so there are no other events to consider here.
When the page loads the first order of business it to get the username from the quesrystring and create the nice little "welcome" phrase that will be inserted over the ad image. After that there are several variables that we will use to do all of the image rendering work. Now, I purposely used more variables than was really necessary so that everyone could see most of the different options available. In some cases for this example I am setting properties equal to their default values for illustrative purposes. Here is a quick breakdown of each variable's purpose:
- MyBitmap - In order to build an image you must convert your base image to a bitmap, add your text, and then output the bitmap to whatever format you like. That makes this variable our blank canvas where we will create our image.
- MyMemoryStream - Instead of our page rendering ASP.NET controls we are creating it to become an image generator using MemoryStream.
- MyGraphics - This is the palette for the image. This variable determines how the graphics will be rendered onto our bitmap canvas. It's the paint type, brush type, etc. for our canvas.
- MyGraphicsPath - This variable handles setting up rendering of text onto the image. It brings together the brush, font, text and format together. It's the sketch before the paint is applied.
- MyBrush - This is exactly as it sounds. It determines the type of brush used when rendering along with the color.
- ContentStartPoint - Here is the upper left corner position for the work area (defined below).
- ContentWorkArea - This defines the size of the work area where you will be working.
- MyImageRectangle - This puts the content work area and starting point together (defined above). It then becomes the boundaries within your image where your text will be rendered.
- MyFont - This defines everything about the font you will be using for rendering which includes font name, size, size type and style.
- MyFormat -This defines the vertical and horizontal alignment of the text within your rectangle.
Tip: If your image is not rendering the font as you expected it could be any one of three issues. The first is that the font is not installed on the server. The second is that the font is not a TrueType font (TrueType is the only type that will render correctly). Third, the font name is wrong or misspelled.
Now that you know what the variables are we'll do a simple walk-through of the code:
- Build the text from the querystring that will be inserted into the image.
- Define our work area rectangle using our starting point and work area variables. You can redefine this as many times as you like to put text in different areas of the image, however, we are only inserting text into one location.
- Define the font and format for our text.
- Get the base image and convert it into our working bitmap.
- Set the bitmap resolution and graphics settings. The graphics settings in the example will render a high quality image.
- Define the graphics path with our "welcome" string we created above. This includes definitions for font family, font style, font size, our work area rectangle, and text format.
- Use the MyGraphics variable to render our text onto the image.
- Clear and set our response.
- Save our bitmap as png to our memory stream which we then send to the output stream.
- Clean up our mess.
Common QuestionsCan I output in other formats?
All of your standard formats like bitmap, gif, jpeg and png are available. However, for the best combination of quality and small footprint you should use png.
Can I use custom colors instead of the named colors?
You can use any RGB color that you like. You create custom colors using
What happens when the text exceeds the space allotted (MyImageRectangle)?
The class will attempt to wrap within your defined rectangle until it runs out of space. Anything beyond the bounds it simply not rendered. It does not "squish" the text into the allotted space.
I know the scenario is a bit absurd but it served its purpose for the example. In reality creating images can be very useful for specific circumstances. For example, you could easily watermark sample images using this technique for websites that sell stock photography. This would save much time and effort over manually creating images with a watermark. It would also allow you to automatically keep the copyright year in the watermark up to date. Another use (this one I have implemented before) might be for a printing company that wants to allow its users to preview custom print products before they order. Even though the practical applications are limited, the ability to create images on the fly can be a very powerful tool.
Note: The example here was built in ASP.NET 2.0. However, the classes used are included in ASP.NET 1.1 through 4.0 and have remained virtually unchanged since ASP.NET 1.1.