Thursday, January 8, 2009

SSRS Reports Rotate Text Or Split Alphabet Per Line

Few days ago my client requested a report, which required rotation of text and alphabet by 45 degree. Expected output was something Like this:





















Many of you may know that you can rotate a text in textbox by setting WritingMode to "tb-rl", which will rotate the text and it will look something like this:

















In order to rotate text at desired angle do following:
(1) create an image with desired text
(2) rotate that image
(3) import it to the image box in report

Add System.Drawing to the references by going to the Report Properties -> References.

for step1 use following function:
Function CreateImage(ByVal sImageText As String, ByVal FontType As String, ByVal FontSize As Integer) As System.Drawing.Bitmap
'Create an image from scratch
Dim bmpImage As New Drawing.Bitmap(1, 1)
Dim iWidth As Integer = 0
Dim iHeight As Integer = 0
'Create the Font object for the image text drawing.
Dim MyFont As New Drawing.Font(FontType, FontSize, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point)
'Create a graphics object to measure the text's width and height.
Dim MyGraphics As Drawing.Graphics = Drawing.Graphics.FromImage(bmpImage)
'This is where the bitmap size is determined.
iWidth = MyGraphics.MeasureString(sImageText, MyFont).Width
iHeight = MyGraphics.MeasureString(sImageText, MyFont).Height
'// Create the bmpImage again with the correct size for the text and font.
bmpImage = New Drawing.Bitmap(bmpImage, New Drawing.Size(iWidth, iHeight))
'// Add the colors to the new bitmap.
MyGraphics = Drawing.Graphics.FromImage(bmpImage)
MyGraphics.Clear(Drawing.Color.White)
MyGraphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
MyGraphics.DrawString(sImageText, MyFont, New Drawing.SolidBrush(Drawing.Color.Black), 0, 0)
MyGraphics.Flush()
Return bmpImage
End Function






for step2 use following function:



Function RotateImage(ByVal bm_in As System.Drawing.Bitmap, ByVal RotationAngle As Integer) As System.Drawing.Bitmap
Dim wid As Single = bm_in.Width
Dim hgt As Single = bm_in.Height
Dim corners As System.Drawing.Point() = { _
New System.Drawing.Point(0, 0), _
New System.Drawing.Point(wid, 0), _
New System.Drawing.Point(0, hgt), _
New System.Drawing.Point(wid, hgt)}
' Translate to center the bounding box at the origin.
Dim cx As Single = wid / 2
Dim cy As Single = hgt / 2
Dim i As Long
For i = 0 To 3
corners(i).X -= cx
corners(i).Y -= cy
Next i
' Rotate.
Dim theta As Single = Single.Parse(RotationAngle) * System.Math.PI _
/ 180.0
Dim sin_theta As Single = System.Math.Sin(theta)
Dim cos_theta As Single = System.Math.Cos(theta)
Dim X As Single
Dim Y As Single
For i = 0 To 3
X = corners(i).X
Y = corners(i).Y
corners(i).X = X * cos_theta + Y * sin_theta
corners(i).Y = -X * sin_theta + Y * cos_theta
Next i
' Translate so X >= 0 and Y >=0 for all corners.
Dim xmin As Single = corners(0).X
Dim ymin As Single = corners(0).Y
For i = 1 To 3
If xmin > corners(i).X Then xmin = corners(i).X
If ymin > corners(i).Y Then ymin = corners(i).Y
Next i
For i = 0 To 3
corners(i).X -= xmin
corners(i).Y -= ymin
Next i
' Create an output Bitmap and Graphics object.
Dim bm_out As New System.Drawing.Bitmap(CInt(-2 * xmin), CInt(-2 * _
ymin))
Dim gr_out As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bm_out)
' Drop the last corner lest we confuse DrawImage,
' which expects an array of three corners.
ReDim Preserve corners(2)
' Draw the result onto the output Bitmap.
gr_out.Clear(Drawing.Color.White)
gr_out.DrawImage(bm_in, corners)
gr_out.Flush()
' Display the result.
Return bm_out
End Function






for step3 use following function:



Function StreamImage(ByVal bmpImage As System.Drawing.Bitmap)
Dim stream As IO.MemoryStream = New IO.MemoryStream
Dim bitmapBytes As Byte()
'Create bitmap stream
bmpImage.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg)
bitmapBytes = stream.ToArray
stream.Close()
bmpImage.Dispose()
Return bitmapBytes
End Function
Finally execute all together:
Use following code:
Function Create_RotateAndStreamImage(ByVal sImageText As String, ByVal FontType As String, ByVal FontSize As Integer, ByVal RotationAngle As Integer)
'Create a blank image
Dim bmpImage As New Drawing.Bitmap(1, 1)
'Create an image from scratch
bmpImage = CreateImage(sImageText, FontType, FontSize)
'Rotate Image at desired Angle
bmpImage = RotateImage(bmpImage, RotationAngle)
Return StreamImage(bmpImage)
Return bmpImage
End Function
It gives desired results
buy using following expression:

= Code.Create_RotateAndStreamImage("TEJAS", "Microsoft Sans Serif", 14, 45)


Incase some of you may want to get following outcome:






So, now you have to rotate the alphabets, there isn't a builtin function available to rotate alphabets.
Anohter way is do not rotate text, just split word in to aphabet per line. You can use "& vbCrLf & " in between two alphabet to go to next line.


I used following vb code to do that:
Public Function Split(Word As String) As String
Dim TempWord As String
Dim SplitTemp As String
Split = ""
TempWord = Word
Do While Len(TempWord) > 0
SplitTemp = Left(TempWord, 1)
Split = Split & vbCrLf & SplitTemp
TempWord = Right(TempWord, Len(TempWord) - 1)
Loop
End Function

And use following expression:

=Code.Split("TEJAS")

8 comments:

  1. So I'm trying to use this code, and know little about Trig, or drawing objects. That's making it difficult for me to do what I'm wanting to do. See, Reporting Services won't center images. I need this image to be a set size, 8 imches by 1.3 inches, and to have the text centered in it. Any hints?

    ReplyDelete
  2. Ah, I just found it...

    The change is in the CreateImage function...
    Here's mine....

    Function CreateImage(ByVal sImageText As String, ByVal FontType As String, ByVal FontSize As Integer) As System.Drawing.Bitmap
    'Create an image from scratch
    Dim bmpImage As New Drawing.Bitmap(1, 1)
    Dim iWidth As Integer = 0
    Dim iHeight As Integer = 0
    'Create the Font object for the image text drawing.
    Dim MyFont As New Drawing.Font(FontType, FontSize, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point)
    'Create a graphics object to measure the text's width and height.
    Dim MyGraphics As Drawing.Graphics = Drawing.Graphics.FromImage(bmpImage)
    'This is where the bitmap size is determined.
    'DPS - I just made it a static 800x142, roughly 8 inches by 1.3 inches.
    iWidth = 800
    iHeight = 142
    '// Create the bmpImage again with the correct size for the text and font.
    bmpImage = New Drawing.Bitmap(bmpImage, New Drawing.Size(iWidth, iHeight))
    '// Add the colors to the new bitmap.
    MyGraphics = Drawing.Graphics.FromImage(bmpImage)
    MyGraphics.Clear(Drawing.Color.White)
    MyGraphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
    'DPS - Here I modified the location of where the DrawString drops the image. Now it drops it in place to be centered in the iWidth and iHeight
    MyGraphics.DrawString(sImageText, MyFont, New Drawing.SolidBrush(Drawing.Color.Black), _
    (iWidth/2) - (MyGraphics.MeasureString(sImageText, MyFont).Width/2), _
    (iHeight/2) - (MyGraphics.MeasureString(sImageText, MyFont).Height/2))
    MyGraphics.Flush()
    Return bmpImage
    End Function


    I commended my additions, with DPS in the front of the comment.

    ReplyDelete
  3. Awesome little function, by the way, thanks for the source.

    ReplyDelete
  4. Hi Tejas,

    I have issue using this code. This error i am getting

    the value expression used in image "image1" return data type is not valid.

    I used image control and added =code.creat.... in expression.

    Also i added reference and added your all code in Code box.

    Please let me know how to solve this.

    Thanks,
    Akilan.

    ReplyDelete
  5. Works fine but always kinda blurry...

    Had to adjust the last two functions : As Byte() and delete "Return bmpImage" from the last function since there's two returns.

    ReplyDelete
  6. Just a note, You have to change your image source to "Database" and specify the mimetype. The blurryiness is because of the Anti-Alias rendering hint

    ReplyDelete
  7. I have to just expand table detail horizontally what should I do...??

    ReplyDelete
  8. is it possible to rotate the border of the text box also to 45 degree , as rotating the text will not accommodate in the text box.

    ReplyDelete