Foxite.COM Community Weblog

Foxite.COM Community Weblog - free weblog service for the Visual FoxPro Community.
Welcome to Foxite.COM Community Weblog Sign in | Join | Help
in
Home Blogs Forum Photos Forum Archives

VFP IMAGING



DRAW ROTATED STRINGS WITH GDIPLUS-X

Gdi+ provides many ways to draw rotated objects, like images, strings and shapes. Basically, we need to do a translate transformation to the location of the text, then a rotate transform through the required angle, and then writing the text as normal using drawstring.

 

This is the definition found in MSDN: "The rotation operation consists of multiplying the transformation matrix by a matrix whose elements are derived from the angle parameter. This method applies the rotation by prepending it to the transformation matrix."

 

IMPORTANT:

All samples below use the new GDIPlus-X library, that is still in ALPHA version, but is already stable and reliable to do the majority of GDI+ tasks. Download the latest stable release from Codeplex:

http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX

 

* Initialize GdiPlus-X library
DO LOCFILE("System.App")

WITH _SCREEN.SYSTEM.Drawing

* Create a new and empty Bitmap
LOCAL loBmp as xfcBitmap
loBmp = .Bitmap.New(180,180)

* Create a Graphics object associated to the bitmap
LOCAL loGfx as xfcGraphics
loGfx = .Graphics.FromImage(loBmp)

* Clear the BackGround of the image with light blue
loGfx.Clear(.Color.FromRgb(230,230,255))

* Create a SolidBrush with Red Color
LOCAL loBrush AS xfcBrush
loBrush = .SolidBrush.New(.COLOR.FromRgb(255,0,0))
* The above statement could be also :
* loBrush = .SolidBrush.New(.COLOR.FromARgb(255,255,0,0))
* loBrush = .SolidBrush.New(.COLOR.Red)

* Create a Rectangle in which the rotated text will be drawn
LOCAL loRect AS xfcRectangle
loRect = .Rectangle.New(0, 0, loBmp.Width, loBmp.Height)

* Get a basic string format object, then set properties
LOCAL loStringFormat AS xfcStringFormat
loStringFormat = .StringFormat.New()
loStringFormat.ALIGNMENT = .StringAlignment.CENTER
loStringFormat.LineAlignment = .StringAlignment.CENTER

* Create a Font object
LOCAL loFont AS xfcFont
loFont = .FONT.New("Verdana",16, .FontStyle.Bold, .GraphicsUnit.POINT)

* After creating all needed objects, we can apply the rotation
* and draw the text

* Translate and Rotate
loGfx.TranslateTransform(loBmp.Width /2, loBmp.Height /2)
loGfx.RotateTransform(-45) && angle of 45 degrees
loGfx.TranslateTransform(-loBmp.Width /2, -loBmp.Height /2)

* Finally, draw the string
loGfx.DrawString("Rotated Text" + CHR(13) + CHR(10) + "GDIPlus-X is COOL !!!", ;
loFont, loBrush, loRect, loStringFormat)

* Reset Rotation
loGfx.ResetTransform()

* Save image to File
loBmp.Save("c:\rotated.png", .Imaging.ImageFormat.Png)

ENDWITH

* Show created image
RUN /N explorer.exe c:\rotated.png

TIME TO PLAY

For the above sample I used a cool trick to rotate the text at the center of the image, and not at the left edge, that is the default behavior. Now it's up to you to make some tests. Try omitting the two lines that call the Graphics.TranslateTransform, change the angle, using negative and positive angles, etc...

* Translate and Rotate

loGfx.TranslateTransform(loBmp.Width /2, loBmp.Height /2)

loGfx.RotateTransform(-45) && angle of 45 degrees

loGfx.TranslateTransform(-loBmp.Width /2, -loBmp.Height /2)

In the "Samples" folder of the latest GDIPlus-X library version there's another cool sample, "Rotation.Scx", in which another translation technique was used. All the codes are in the "BeforeDraw" method of the ImageCanvas object.

 

DRAW ROTATED STRINGS IN YOUR REPORTS !

The real reason for this post was a request from my friend from Foxbrasil Emerson Santon Reed, when he asked about creating a watermark of a big text rotated in 45 degrees in a report.

So, my part was just to adapt the GDI+ code shown above to the ReportListener class and Report that he provided.

Run the code below to see a rotated text centered at 45 degrees in a report:

* Create a sample Report
LOCAL i
CREATE CURSOR dummy (fld1 c(20), fld2 c(15))
FOR i=1 TO 100
  INSERT INTO dummy VALUES ("ReportListener with GdiPlus-X", "Visit CodePlex")
ENDFOR
SELECT
dummy
CREATE REPORT _testreport FROM dummy

* Init GdiPlus-X
DO LOCFILE("System.App")

* Load Listener class
LOCAL loreportlistener
loreportlistener = CREATEOBJECT("MyReportListener")
loreportlistener.LISTENERTYPE = 1

* Call the report using our listener
REPORT FORM _testreport OBJECT loreportlistener
USE IN dummy
RETURN


DEFINE CLASS myreportlistener AS _reportlistener OF ;
    ADDBS(HOME()) + "FFC\" + "_ReportListener.VCX"
  newPage = .T.
  oG
digraphics = NULL

  FUNCTION BEFOREREPORT
    DODEFAULT
()
    This.ogdigraphics = _SCREEN.SYSTEM.drawing.graphics.new()
  ENDFUNC

  FUNCTION BEFOREBAND(nbandobjcode, nfrxrecno)
  #DEFINE FRX_OBJCOD_PAGEHEADER               1

  IF nbandobjcode==frx_objcod_pageheader
      This.NewPage = .T.
      IF NOT This.issuccessor
        This.sharedgdiplusgraphics = This.GDIPLUSGRAPHICS
      ENDIF
      This.ogdigraphics.handle = This.sharedgdiplusgraphics
  ENDIF
  DODEFAULT(nBandObjCode, nFRXRecNo)
  ENDFUNC

  PROCEDURE RENDER(nfrxrecno,;
     nleft,ntop,nwidth,nheight,;
     nobjectcontinuationtype, ;
     ccontentstoberendered, gdiplusimage)

     WITH _SCREEN.SYSTEM.drawing

     IF This.NewPage
        * Create a SolidBrush with Red Color

        LOCAL lobrush AS xfcbrush
        lobrush = .solidbrush.new(.COLOR.fromRgb(255,64,64))

        * Create a Rectangle in which the rotated text will be drawn
        LOCAL lorect AS xfcrectangle
        lorect = .rectangle.new(0, 0, This.sharedpagewidth,;
          This.sharedpageheight)

        * Get a basic string format object, then set properties
        LOCAL lostringformat AS xfcstringformat
        lostringformat = .stringformat.new()
        lostringformat.ALIGNMENT = .stringalignment.CENTER
        lostringformat.linealignment = .stringalignment.CENTER

        * Create a Font object
        LOCAL lofont AS xfcfont
        lofont = .FONT.new("Verdana",48, 0, .graphicsunit.POINT)

        * Translate and Rotate
        This.ogdigraphics.translatetransform(This.sharedpagewidth/2,;
          This.sharedpageheight/2)
        This.ogdigraphics.rotatetransform(-45)
        This.ogdigraphics.translatetransform(-This.sharedpagewidth/2,;
          -This.sharedpageheight/2)
        This.ogdigraphics.drawstring("Rotated Text" +CHR(13)+CHR(10)+;
          "GDIPlus-X is COOL !!!", ;
          lofont, lobrush, lorect, lostringformat)

        * Reset Rotation
        This.ogdigraphics.resettransform

        This.NewPage = .F.

     ENDIF
     ENDWITH
     DODEFAULT(nfrxrecno,;
        nleft,ntop,nwidth,nheight,;
        nobjectcontinuationtype, ;
        ccontentstoberendered, gdiplusimage)

  ENDPROC
ENDDEFINE

UPDATE 06-08-31

To make sure that the watermark will not be overwritten by opaque controls, the code below does the same thing shown above, but this time draws the watermark only after the footer band is totally rendered. The color used for this case was also changed from opaque to semitransparent, using Alpha of 128 ( 0 = totally transparent, 255 = opaque). So, here's the new ReportListener subclass to deal with this problem:

DEFINE CLASS myreportlistener AS _reportlistener OF ;
    ADDBS(HOME()) + "FFC\" + "_ReportListener.VCX"
  ogdigraphics = NULL

  FUNCTION BEFOREREPORT
    DODEFAULT
()
    This.ogdigraphics = _Screen.System.Drawing.Graphics.New()
  ENDFUNC

  FUNCTION AFTERBAND(nbandobjcode, nfrxrecno)
  *-- FRX OBJCODE column values
  #DEFINE FRX_OBJCOD_TITLE                    0
  #DEFINE FRX_OBJCOD_PAGEHEADER               1
  #DEFINE FRX_OBJCOD_COLHEADER                2
  #DEFINE FRX_OBJCOD_GROUPHEADER              3
  #DEFINE FRX_OBJCOD_DETAIL                   4
  #DEFINE FRX_OBJCOD_GROUPFOOTER              5
  #DEFINE FRX_OBJCOD_COLFOOTER                6
  #DEFINE FRX_OBJCOD_PAGEFOOTER               7
  #DEFINE FRX_OBJCOD_SUMMARY                  8
  #DEFINE FRX_OBJCOD_DETAILHEADER             9
  #DEFINE FRX_OBJCOD_DETAILFOOTER            10

  IF nbandobjcode==frx_objcod_pagefooter
      IF NOT This.issuccessor
        This.sharedgdiplusgraphics = This.GDIPLUSGRAPHICS
      ENDIF
      This.ogdigraphics.handle = This.sharedgdiplusgraphics
    
      WITH _SCREEN.SYSTEM.drawing
        * Create a SolidBrush with Red semi transparent color
        LOCAL lobrush AS xfcbrush
        lobrush = .solidbrush.new(.COLOR.fromArgb(128,255,128,128))

        * Create a Rectangle in which the rotated text will be drawn
        LOCAL lorect AS xfcrectangle
        lorect = .rectangle.new(0, 0, This.sharedpagewidth,;
          This.sharedpageheight)

        * Get a basic string format object, then set properties
        LOCAL lostringformat AS xfcstringformat
        lostringformat = .stringformat.new()
        lostringformat.ALIGNMENT = .stringalignment.CENTER
        lostringformat.linealignment = .stringalignment.CENTER

        * Create a Font object
        LOCAL lofont AS xfcfont
        lofont = .FONT.new("Verdana",48, 0, .graphicsunit.POINT)

        * Translate and Rotate
        This.ogdigraphics.translatetransform(This.sharedpagewidth/2,;
          This.sharedpageheight/2)
        This.ogdigraphics.rotatetransform(-45)
        This.ogdigraphics.translatetransform(-This.sharedpagewidth/2,;
          -This.sharedpageheight/2)
        This.ogdigraphics.drawstring("Rotated Text" +CHR(13)+CHR(10)+;
          "GDIPlus-X is COOL !!!", ;
          lofont, lobrush, lorect, lostringformat)

        * Reset Rotation
        This.ogdigraphics.resettransform()

      ENDWITH
    ENDIF
    DODEFAULT(nBandObjCode, nFRXRecNo)
  ENDFUNC
ENDDEFINE

RELATED LINKS

Article from Bill Wagner  http://www.ftponline.com/vsm/2002_10/online/csharp_bwagner_10_31_02/

GdiPlusX page in CodePlex: http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX

Published Wednesday, August 30, 2006 10:16 AM by cesarchalom
Filed Under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

Emerson Santon Reed said:

Hi friend!
Congratulations, now you're the GDI+ guy!   :)
Tks for help me and share your knowledge with the community.
August 31, 2006 1:12 PM
 

Emerson Santon Reed said:

Hi César.
In this sample, if I put opaque controls on report, the "watermark text" is cutted.
There is a way to put the "watermark text" over all controls and make them semi-transparent?
There is a way to make the "watermark text" stretches to fill the entire rectangle?

Hi Emerson, Thanks for testing.

Yep ! Good catch ! I've just added a new code to the article. In the original aproach that you sent to me, all the process was happening in the Render Event. Now I've moved the code to the "AfterBand" event. I'm drawing only after the Footer is rendered, to make sure that nothing else will be drawn over our watermark.

Please make some more tests, and feel free to call me again if something does not go as expected.

Hope this helps,

Cesar

August 31, 2006 9:01 PM
 

emersonreed said:

Hi Cesar!
The complete solution on how to insert a watermark in a report using report listener was posted in my blog:

http://weblogs.foxite.com/emersonreed/archive/2006/09/13/A_sample_on_how_to_add_features_to_Report_Listener.aspx

Tks César for helping me another time!
September 14, 2006 2:40 AM
 

Cesar Chalom said:

Recently I was navigating at Rick Strahl's Blog, when I found a very interesting post, "A Captcha Image...
December 7, 2006 12:26 AM
 

Cesar Chalom said:

January 10, 2007 1:29 AM
 

Ana María Bisbé said:

Versión en Español de este artículo en / Spanish version at http://www.portalfox.com/article.php?sid=2261
July 24, 2007 7:08 PM

What do you think?

(required) 
(optional)
(required)