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 GitHub:
* 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. oGdigraphics = 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/