RotatedTextHandler Class
Life With Nate
Nate's Poetry Page
Resume (PDF)
Nate's Code
ASP.NET 2.0/3.5
ButtonExtensions
ConditionalRequiredTextValidator
CSSImageMap
CustomStyle
DateTimePicker
DateTimePickerTemplated
LengthValidator
RestrictInputTextBox
RestrictInputValidator
Rollovers
RotatedTextHandler
RotatedTextImage
Rotators
SelectedCountValidator
SlideIntoView
StatesDropDownList
UniqueValueValidatorBase
VB.NET 2.0/3.5
Just For Fun
Windows Phone 7
Description
The RotatedTextHandler class is a Handler (a class that implements System.Web.IHttpHandler) that generates a gif image of rotated text. The RotatedTextHandler class can be used by itself or you can use it with the
RotatedTextImage
control.
Properties & Methods
RotatedTextHandler
The RotatedTextHandler is a Handler that generates a gif of rotated text. The RotatedTextHandler gets registered in the Web Application's Web.config (see comment at beginning of source code), the URL will be of the format /RotatedTextHandler.ashx?property=value. Listed below are the properties that may be included in the querystring passed to RotatedTextHandler.ashx.
Rotation
- The angle, specified in degrees, that the text is to be rotated
Text
- The text that is to be rotated
FontColor
- The color in which the text is to be drawn
BackgroundStyle
- The value of a member of the NathanSokalski.RotatedTextHandler.BackgroundStyle enumeration. This will be 0, 1, or 2
Background
- The color used for the background. This property is ignored if BackgroundStyle has a value of 0 (Transparent)
FontStyle
- An integral value specifying a System.Drawing.FontStyle. This may be any value from 0 to 15
Bold
- A Boolean value specifying whether the text should be bold. This property is ignored if the FontStyle property is used
Italic
- A Boolean value specifying whether the text should be italicized. This property is ignored if the FontStyle property is used
Strikeout
- A Boolean value specifying whether the text should be strikeout. This property is ignored if the FontStyle property is used
Underline
- A Boolean value specifying whether the text should be underlined. This property is ignored if the FontStyle property is used
FontName
- The name of the font to use when drawing the text
Size
- The size to use when drawing the text
BackgroundStyle As Byte
An enumeration used to specify what part of the background should be colored.
Transparent
- None of the background should be colored
TextOnly
- Only the area behind the actual text should be colored. This will end up creating a rotated rectangle in the same position as the text
Complete
- The background of the entire area used by the generated image will be filled
Source Code
RotatedTextHandler.vb:
Imports System.Drawing Imports System.Drawing.Imaging Namespace NathanSokalski Public Class RotatedTextHandler : Implements System.Web.IHttpHandler 'The following code must be included in a Web Application's Web.config in order to use the RotatedTextHandler: '<configuration> ' <system.web> ' <httpHandlers> ' <add path="*/RotatedTextHandler.ashx" verb="*" type="NathanSokalski.RotatedTextHandler,NathanSokalski" validate="false"/> ' </httpHandlers> ' </system.web> '</configuration> Private textfont As System.Drawing.Font Private fontcolor As System.Drawing.Color = Color.Black Private bgstyle As BackgroundStyle = BackgroundStyle.Transparent Private background As System.Drawing.Color Private rotation As Double = 0 Private stringdimensions As System.Drawing.SizeF Private corners(4) As Point 'Index 0 = top left of text Private Sub SetStringDimensions(ByVal txt As String) Me.stringdimensions = Graphics.FromImage(New Bitmap(1, 1, PixelFormat.Format32bppArgb)).MeasureString(txt, Me.textfont) End Sub Private Sub SetRotation(ByVal angle As Double) Me.rotation = angle If Me.rotation < 0 Then While Me.rotation < 0 Me.rotation += 360.0R End While ElseIf Me.rotation >= 360 Then While Me.rotation >= 360 Me.rotation -= 360.0R End While End If End Sub Private Sub SetCorners(ByVal stringdimensions As SizeF) Dim tempangle As Double Select Case Me.rotation Case 0 To 90 tempangle = Me.rotation * Math.PI / 180.0R Me.corners(0).X = CInt(stringdimensions.Height * Math.Sin(tempangle)) Me.corners(0).Y = 0 Me.corners(1).X = CInt(stringdimensions.Width * Math.Cos(tempangle) + Me.corners(0).X) Me.corners(1).Y = CInt(stringdimensions.Width * Math.Sin(tempangle)) Me.corners(2).X = CInt(stringdimensions.Width * Math.Cos(tempangle)) Me.corners(2).Y = CInt(stringdimensions.Height * Math.Cos(tempangle) + Me.corners(1).Y) Me.corners(3).X = 0 Me.corners(3).Y = CInt(stringdimensions.Height * Math.Cos(tempangle)) Case 180 To 270 tempangle = (Me.rotation - 180.0R) * Math.PI / 180.0R Me.corners(0).X = CInt(stringdimensions.Width * Math.Cos(tempangle)) Me.corners(0).Y = CInt(stringdimensions.Width * Math.Sin(tempangle) + stringdimensions.Height * Math.Cos(tempangle)) Me.corners(1).X = 0 Me.corners(1).Y = CInt(stringdimensions.Height * Math.Cos(tempangle)) Me.corners(2).X = CInt(stringdimensions.Height * Math.Sin(tempangle)) Me.corners(2).Y = 0 Me.corners(3).X = CInt(stringdimensions.Width * Math.Cos(tempangle) + Me.corners(2).X) Me.corners(3).Y = CInt(stringdimensions.Width * Math.Sin(tempangle)) Case 90 To 180 'This case is not intended to handle 90 or 180, but these values should be handled by the previous cases tempangle = (Me.rotation - 90.0R) * Math.PI / 180.0R Me.corners(0).X = CInt(stringdimensions.Width * Math.Sin(tempangle) + stringdimensions.Height * Math.Cos(tempangle)) Me.corners(0).Y = CInt(stringdimensions.Height * Math.Sin(tempangle)) Me.corners(1).X = CInt(stringdimensions.Height * Math.Cos(tempangle)) Me.corners(1).Y = CInt(stringdimensions.Width * Math.Cos(tempangle) + Me.corners(0).Y) Me.corners(2).X = 0 Me.corners(2).Y = CInt(stringdimensions.Width * Math.Cos(tempangle)) Me.corners(3).X = CInt(stringdimensions.Width * Math.Sin(tempangle)) Me.corners(3).Y = 0 Case 270 To 360 'This case is not intended to handle 270 or 360, but these values should be handled by the previous cases tempangle = (Me.rotation - 270.0R) * Math.PI / 180.0R Me.corners(0).X = 0 Me.corners(0).Y = CInt(stringdimensions.Width * Math.Cos(tempangle)) Me.corners(1).X = CInt(stringdimensions.Width * Math.Sin(tempangle)) Me.corners(1).Y = 0 Me.corners(2).X = CInt(Me.corners(1).X + stringdimensions.Height * Math.Cos(tempangle)) Me.corners(2).Y = CInt(stringdimensions.Height * Math.Sin(tempangle)) Me.corners(3).X = CInt(stringdimensions.Height * Math.Cos(tempangle)) Me.corners(3).Y = CInt(stringdimensions.Width * Math.Cos(tempangle) + Me.corners(2).Y) End Select End Sub Private Function DrawRotatedText(ByVal txt As String) As Bitmap Dim bmp As New Bitmap(Math.Max(Math.Max(Math.Max(Me.corners(0).X, Me.corners(1).X), Me.corners(2).X), Me.corners(3).X), Math.Max(Math.Max(Math.Max(Me.corners(0).Y, Me.corners(1).Y), Me.corners(2).Y), Me.corners(3).Y), PixelFormat.Format32bppArgb) Dim graphic As Graphics = Graphics.FromImage(bmp) graphic.TextRenderingHint = Text.TextRenderingHint.SingleBitPerPixel Dim transparentcolor As System.Drawing.Color = Color.FromArgb(255 - Me.fontcolor.R, 255 - Me.fontcolor.G, 255 - Me.fontcolor.B) If Me.bgstyle <> BackgroundStyle.Complete Then Dim colormaker As New System.Random() While transparentcolor.Equals(Me.fontcolor) OrElse transparentcolor.Equals(Me.background) transparentcolor = Color.FromArgb(colormaker.Next(256), colormaker.Next(256), colormaker.Next(256)) End While End If If Me.bgstyle = BackgroundStyle.Complete Then graphic.FillRectangle(New SolidBrush(Me.background), 0, 0, bmp.Width, bmp.Height) Else graphic.FillRectangle(New SolidBrush(transparentcolor), 0, 0, bmp.Width, bmp.Height) graphic.TranslateTransform(Me.corners(0).X, Me.corners(0).Y) graphic.RotateTransform(CSng(Me.rotation)) If Me.bgstyle = BackgroundStyle.TextOnly Then graphic.FillRectangle(New SolidBrush(Me.background), 0, 0, Me.stringdimensions.Width, Me.stringdimensions.Height) graphic.DrawString(txt, Me.textfont, New SolidBrush(Me.fontcolor), 0, 0, System.Drawing.StringFormat.GenericDefault) If Me.bgstyle <> BackgroundStyle.Complete Then Dim transparentbmp As New Bitmap(bmp.Width, bmp.Height, PixelFormat.Format8bppIndexed) Dim palette As ColorPalette = transparentbmp.Palette Dim nextindex As Byte = 0 Dim bmpdata As BitmapData = transparentbmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed) Dim index As Integer For i As Integer = 0 To 255 palette.Entries(i) = Color.Empty Next For y As Integer = 0 To bmp.Height - 1 For x As Integer = 0 To bmp.Width - 1 'Get the palette index of the current pixel index = Me.InPalette(palette, nextindex - 1, bmp.GetPixel(x, y)) 'If the color is not in the palette, add it at the next unused index If index = -1 Then palette.Entries(nextindex) = bmp.GetPixel(x, y) index = nextindex nextindex += CByte(1) End If 'Set the pixel to the proper index System.Runtime.InteropServices.Marshal.WriteByte(bmpdata.Scan0, y * bmpdata.Stride + x, CByte(index)) Next Next transparentbmp.UnlockBits(bmpdata) 'If the specified transparent color is included in the palette, change that color to transparent If Me.InPalette(palette, nextindex - CByte(1), transparentcolor) <> -1 Then palette.Entries(Me.InPalette(palette, nextindex - CByte(1), transparentcolor)) = Color.FromArgb(0, 0, 0, 0) transparentbmp.Palette = palette Return transparentbmp Else Return bmp End If End Function Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest context.Response.ContentType = "image/gif" context.Response.Buffer = False 'Set Font Property Dim tempstyle As New FontStyle Dim tempfontname As String = "Arial" Dim tempfontsize As Single = 24 If String.IsNullOrEmpty(context.Request.QueryString("FontStyle")) Then If Not String.IsNullOrEmpty(context.Request.QueryString("Bold")) AndAlso CBool(context.Request.QueryString("Bold")) Then tempstyle = tempstyle Or FontStyle.Bold If Not String.IsNullOrEmpty(context.Request.QueryString("Italic")) AndAlso CBool(context.Request.QueryString("Italic")) Then tempstyle = tempstyle Or FontStyle.Italic If Not String.IsNullOrEmpty(context.Request.QueryString("Strikeout")) AndAlso CBool(context.Request.QueryString("Strikeout")) Then tempstyle = tempstyle Or FontStyle.Strikeout If Not String.IsNullOrEmpty(context.Request.QueryString("Underline")) AndAlso CBool(context.Request.QueryString("Underline")) Then tempstyle = tempstyle Or FontStyle.Underline Else tempstyle = CType(CInt(context.Request.QueryString("FontStyle")), FontStyle) End If If Not String.IsNullOrEmpty(context.Request.QueryString("FontName")) Then tempfontname = context.Request.QueryString("FontName") If Not String.IsNullOrEmpty(context.Request.QueryString("Size")) Then tempfontsize = CSng(context.Request.QueryString("Size")) Me.textfont = New Font(tempfontname, tempfontsize, tempstyle, GraphicsUnit.Pixel) 'Set the dimensions of the final image and upper left point of the text Me.SetStringDimensions(context.Request.QueryString("Text")) Me.SetRotation(CDbl(context.Request.QueryString("Rotation"))) Me.SetCorners(Me.stringdimensions) 'Set the font and background colors If Not String.IsNullOrEmpty(context.Request.QueryString("FontColor")) Then Me.fontcolor = System.Drawing.ColorTranslator.FromHtml(context.Request.QueryString("FontColor")) If Not String.IsNullOrEmpty(context.Request.QueryString("BackgroundStyle")) Then Me.bgstyle = CType(CByte(context.Request.QueryString("BackgroundStyle")), BackgroundStyle) If Me.bgstyle <> BackgroundStyle.Transparent Then Me.background = System.Drawing.ColorTranslator.FromHtml(context.Request.QueryString("Background")) End If Me.DrawRotatedText(context.Request.QueryString("Text")).Save(context.Response.OutputStream, ImageFormat.Gif) End Sub ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return True End Get End Property 'Returns index of color in palette or -1 Private Function InPalette(ByVal palette As ColorPalette, ByVal maxindex As Integer, ByVal colortofind As Color) As Integer For i As Integer = 0 To maxindex If palette.Entries(i).ToArgb() = colortofind.ToArgb() Then Return CInt(i) Next Return -1 End Function Public Enum BackgroundStyle As Byte Transparent TextOnly Complete End Enum End Class End Namespace
(NOTE: Be sure to register the handler in the <httpHandlers> section of the Web Application's Web.config file as noted in the comment at the beginning of the source code)
Remarks
In most cases, I recommend using the
RotatedTextImage
control rather than create the URL by hand.