As I've shown in a previous post from last year, this is totally possible for us in VFP, using the GdiPlusX library together with ReportListeners, remember?

BUT I've been receiving some emails about the Full-Justified ReportListener I published last year.
It did not deal with the possibility of multipage reports. In fact, when a string that had to be printed in FullJustified mode had to be divided in more than one page, in the next page the whole string was redrawn.
To solve this, I needed to make some adjustments in the report listener source, more specifically in the "Render" event, in order to deal with the parameters "nObjectContinuationType" and "cContentsToBeRendered". I had totally missed them. In the first version, I was only using the "Text" property that I retrieved in the "EvaluateContents" method. The first tells us if the text could be totally drawn, or partially. The 2nd, brings the text that is to be drawn, not the whole text.
It's amazing how we can control our output with all these new enhancements from the VFP9 Reporting System. In fact, we can control almost everything !
It's a pity I couldn't explore this tool as much as I would like, the possibilities seem to be infinite.
In HELP I found all the info I needed to fix it:
- nObjectContinuationType
* Indicates the current continuation state for the rendered element. When layout elements span pages, they are rendered in multiple sections (once for each page).
* Value Continuation Type
* 0 Complete (no continuation).
* 1 Start of layout element occurrence, will not finish on the current page.
* 2 Mid-element, neither started nor finished on the current page.
* 3 End of element, completed on the current page.
- cContentsToBeRendered
* Indicates the text to be rendered for Expression (Field) and Label layout elements.
* If your derived class sends the text value through some additional processing, such as storage in a table, you can use the STRCONV() function, and its optional regional script parameter, to convert the string to DBCS first. For more information, see STRCONV( ) Function.
Some important tweaks and fixes were needed in the GdiPlusX library too, more specifically in the xfcGraphics.DrawStringJustified function. I added to it a new parameter, "tlJustifyLast", in order fo force the justifying in the last sentence. This is for the case of reports, when an unfinished sentence needs to be justified as well.
The source code is below, you can adapt it to your needs !
The idea is to add a "<FJ>" tag in the USER tab or in the beginning of any string from a textbox in a report, to tell the ReportListener that it will draw the text using the DRAWSTRINGJUSTIFIED method from GdiPlusX. Special thanks to Victor Espinoza, from Miami, FL, for his important feedback, suggestions and fixes.
The "FullJustifyListener" performs the following actions:
- Initializes GdiPlusX
- Creates a GDI+ Graphics object that will be used to draw in the report
- Stores in an array the required information needed to draw the string(Font, Size, Style and Color)
- Before Rendering the string, checks if the "<FJ>" tag is at the beginning of text or at the "USER" tab in the report designer - if yes, draws the string using the new method.
Here are the steps for you to bring this to your reports:
1 - Download the updated GdiPlusX library - the System.App file
VERY IMPORTANT - READ THIS !
This report listener needs the updated version of the GdiPlusX main file of the library, "System.App". As it will take some time till a new version will be released, I've uploaded this file separately, so that people can try this feature from now.
Go to this link: http://www.codeplex.com/VFPX/Release/ProjectReleases.aspx?ReleaseId=8606
And download the file "GdiPlusX Updated System.App for FullJustified purposes"
If you prefer, this file is found in the downloadable source code from this article.
2 - Save the ReportListener:
Please Copy and Paste the code below, and save it as FJLISTENER.PRG in the samples folder of GdiPlusX
* Program : FJLISTENER.PRG
* Version : 2.0
* Purpose : Provides a Report Listener that allows rendering text in
* Full Justify alignment.
* Authors : Cesar Ch http://weblogs.foxite.com/vfpimaging
* Class based on article "Listening to a report" by Doug Hennig
* New tweaked version allows drawing strings that need to be divided in more than one page
* Special thanks to Victor Espinoza
* http://msdn2.microsoft.com/en-us/library/ms947682.aspx
DEFINE CLASS FullJustifyListener AS FXListener
oGDIGraphics = NULL
nSaveGraphicsHandle = 0
nTimes = 1
DIMENSION aRecords[1]
* Before we run the report, go through the FRX and store information about any
* field with our expected directive in its USER memo into the aRecords array.
FUNCTION BEFOREREPORT
DODEFAULT()
* Check if we already have the "System" object in "_Screen"
IF NOT PEMSTATUS(_Screen,"System",5)
DO LOCFILE("System.App")
ENDIF
WITH This
.oGDIGraphics = _SCREEN.SYSTEM.Drawing.Graphics.New()
.SetFRXDataSession()
DIMENSION .aRecords[reccount(), 13]
SCAN FOR "<FJ>" $ UPPER(User)
.aRecords[recno(), 13] = "FJ"
ENDSCAN
.ResetDataSession()
ENDWITH
ENDFUNC
FUNCTION BEFOREBAND(nBandObjCode, nFRXRecNo)
This.SharedGDIPlusGraphics = This.GDIPLUSGRAPHICS
This.oGDIGraphics.Handle = This.SharedGDIPlusGraphics
DODEFAULT(nBandObjCode, nFRXRecNo)
ENDFUNC
PROCEDURE RENDER(tnFRXRecNo,;
tnLeft,tnTop,tnWidth,tnHeight,;
nObjectContinuationType, ;
cContentsToBeRendered, GDIPlusImage)
LOCAL lcText, llFlag
llFlag = .F.
lcText = This.aRecords(tnFRXRecNo,1)
IF (VARTYPE(lcText) = "C" AND LEFT(lcText,4) = "<FJ>") OR ;
(VARTYPE(This.aRecords(tnFRXRecNo,13)) = "C" AND This.aRecords(tnFRXRecNo,13) == "FJ")
IF nObjectContinuationType > 0
* nObjectContinuationType
* -----------------------
* Indicates the current continuation state for the rendered element. When layout elements span pages, they are rendered in multiple sections (once for each page).
* Value Continuation Type
* 0 Complete (no continuation).
* 1 Start of layout element occurrence, will not finish on the current page.
* 2 Mid-element, neither started nor finished on the current page.
* 3 End of element, completed on the current page.
* cContentsToBeRendered
* ---------------------
* Indicates the text to be rendered for Expression (Field) and Label layout elements.
* If your derived class sends the text value through some additional processing, such as storage in a table, you can use the STRCONV() function, and its optional regional script parameter, to convert the string to DBCS first. For more information, see STRCONV( ) Function.
lcText = STRCONV(cContentsToBeRendered,6)
IF INLIST(nObjectContinuationType, 1, 2)
llFlag = .T.
ENDIF
ENDIF
IF UPPER(LEFT(lcText,4)) = "<FJ>"
lcText = SUBSTR(lcText,5) && Remove the <FJ> tag from string
ENDIF
WITH _SCREEN.SYSTEM.Drawing
This.oGDIGraphics.Handle = This.GDIPlusGraphics
*!* Create a GDI+ Rectangle which specifies where on the
*!* surface we're drawing the text.
LOCAL loRectF as xfcRectangleF
loRectF = .RectangleF.New(tnLeft, tnTop, tnWidth, tnHeight)
LOCAL loFont as xfcFont
loFont = .Font.New(This.aRecords(tnFRXRecNo,2) ;
, This.aRecords(tnFRXRecNo,4), This.aRecords(tnFRXRecNo,3) ;
, .GraphicsUnit.Point)
* Retrieve colors for the background
LOCAL lnRed, lnGreen, lnBlue, lnAlpha
lnRed = This.aRecords[tnFRXRecno,5]
lnGreen = This.aRecords[tnFRXRecno,6]
lnBlue = This.aRecords[tnFRXRecno,7]
lnAlpha = This.aRecords[tnFRXRecno,8]
LOCAL loBackBrush as xfcSolidBrush
loBackBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
This.oGdiGraphics.FillRectangle(loBackBrush, tnLeft, tnTop, tnWidth, tnHeight)
* Retieve colors for the Text
lnRed = This.aRecords[tnFRXRecno,9]
lnGreen = This.aRecords[tnFRXRecno,10]
lnBlue = This.aRecords[tnFRXRecno,11]
lnAlpha = This.aRecords[tnFRXRecno,12]
LOCAL loTextBrush as xfcSolidBrush
loTextBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
This.oGdiGraphics.DrawStringJustified(lcText, loFont, loTextBrush, loRectF, llFlag)
ENDWITH
ELSE
*!* If we're not drawing a full justified string,
*!* let Fox draw the text as usual.
DODEFAULT(tnFRXRecNo, tnLeft, tnTop, tnWidth, tnHeight, ;
nObjectContinuationType, cContentsToBeRendered, GDIPlusImage)
ENDIF
*!* Since we already drew the text, we don't want the default
*!* behavior to occur.
NODEFAULT
ENDPROC
FUNCTION EvaluateContents(tnFRXRecno, toObjProperties)
* Get the FRX data
This.aRecords[tnFRXRecno,1] = toObjProperties.Text
This.aRecords[tnFRXRecno,2] = toObjProperties.FontName
This.aRecords[tnFRXRecno,3] = toObjProperties.FontStyle
This.aRecords[tnFRXRecno,4] = toObjProperties.FontSize
This.aRecords[tnFRXRecno,5] = toObjProperties.FillRed
This.aRecords[tnFRXRecno,6] = toObjProperties.FillGreen
This.aRecords[tnFRXRecno,7] = toObjProperties.FillBlue
This.aRecords[tnFRXRecno,8] = toObjProperties.FillAlpha
This.aRecords[tnFRXRecno,9] = toObjProperties.PenRed
This.aRecords[tnFRXRecno,10] = toObjProperties.PenGreen
This.aRecords[tnFRXRecno,11] = toObjProperties.PenBlue
This.aRecords[tnFRXRecno,12] = toObjProperties.PenAlpha
ENDFUNC
ENDDEFINE
3 - Update the report:
Below is a short tutorial for the beginners:
- Open any of your reports, that contains a field of more than one line that will receive the effect. Double-Click on that field, select the GENERAL tab, and add BEFORE your expression, this simple string: [ "<FJ>" + ], like in the picture below.

Another better option is to add the tag to the USER tab, that is available only in VFP9 Report Designer. Just like before, double-click in the desired field, select the "OTHER" tab, then click on the "EDIT USER DATA" button, and add the <FJ> tag to the window, just like the picture below. This is the most recommended approach, because the original data will not be affected, and if one day you decide to stop using the report listener, your report data will not be affected.

4 - Run the report
* Tell VFP that we'll be using the new report features
SET REPORTBEHAVIOR 90
LOCAL loReportListener
loReportListener = CREATEOBJECT("FullJustifyListener")
loReportListener.ListenerType = 1
REPORT FORM YourReport OBJECT loReportListener
Below a last screenshot, showing the of the continued field issue solved:

Enjoy !!!
CLICK HERE TO DOWNLOAD THE SOURCE CODE AND SAMPLE FROM THIS ARTICLE