En esta segunda entrega, Cesar extiende la solución mostrada antes para justificar textos, brindando ahora la posibilidad de hacerlo en informes de VFP.
Dar justificación completa a textos en informe con GdiPlus X
Artículo original: Full Justified Texts in your reports with GdiPlus X
http://weblogs.foxite.com/cesarchalom/archive/2007/04/05/3641.aspx
Autor: Cesar Chalom (http://weblogs.foxite.com/cesarchalom)
Traducido por: Ana María Bisbé York (amby@telefonica.net)
Para: PortalFox (http://www.portalfox.com)
Esta es la continuación a mi entrada anterior, "Full-Justified Text with GdiPlusX". Ahora mostraré cómo podemos utilizar el nuevo método DRAWSTRINGJUSTIFIED de la biblioteca GdiPlusX para justificar completamente textos en nuestros informes.

Existen dos vías para utilizar esta característica en informes.
Lo más obvio es utilizar la imagen guardada en ejemplos del escrito anterior, y utilizarla directamente en el informe.
Pero VFP9 ofrece además muchas otras posibilidades que no podemos dejar sin probar, por lo que decidí crear una subclase del Report Listener que transformaría algunos de los textos de mis informes en Totalmente justificados. Mi idea era que el usuario podría necesitar agregar una etiqueta "<FJ>" en el inicio de cualquier cadena de un cuadro de texto en un informe, para decirle a mi ReportListener que dibujara el texto utilizando el método DRAWSTRINGJUSTIFIED de GdiPlusX.
Tenga en cuenta por favor, que NO soy un experto en Informes asistidos por objetos en VFP 9. Mi experiencia utilizando los Report Listeners es muy limitada. Estoy seguro de que el report listener que se muestra debajo puede ser mejorado, así que si tiene cualquier sugerencia que pueda mejorar la calidad del proceso o hacerlo más sencillo, ¡ dígamelo por favor !
¡ A los gurús de informes ! Colin, Lisa, Doug, Cathy, Dorin, Garret, Craig, Bo y otros... estaría muy agradecido si pudieran echar un vistazo al código que está debajo, y mandar sus comentarios ... ¡ y sugerencias !
En cualquier caso, esto funciona bien.
Mi "FullJustifyListener" realiza las siguientes acciones:
- Inicializa GdiPlusX
- Crea un objeto Graphics GDI+ que será utilizado para dibujar en el informe
- Guarda en una matriz la información requerida para dibujar la cadena (Font, Size, Style y Color)
- Antes de generar la cadena, verifica si la etiqueta "<FJ>" está al inicio del texto, si está, dibuja la cadena utilizando el nuevo método.
MUY IMPORTANTE - ¡ LEA ESTO !
Todos los ejemplos requieren tener la última versión, 0.08A que está en planes de ser liberada. Está un poco oculto en Codeplex, por lo que apunto el enlace directo para la descarga:
http://www.codeplex.com/VFPX/Release/ProjectReleases.aspx?ReleaseId=1711
La parte principal de GDI+ del report listener está ubicada en la subclase "Render".
Por favor, copie y pegue el código que está a continuación y guárdelo como FJLISTENER.PRG en la carpeta Samples de GdiPlusX
DEFINE CLASS FullJustifyListener AS _ReportListener OF ;
ADDBS(HOME(1)) + "FFC\_ReportListener.VCX"
oGDIGraphics = NULL
DIMENSION aRecords[1]
* Antes de ejecutar el informe, necesitamos estar seguros de que fue inicializado GdiPlusX
* y crear un nuevo objeto Graphics
FUNCTION BEFOREREPORT
DODEFAULT()
WITH This
* Verificar si ya tenemos el objeto "System" en "_Screen"
IF NOT PEMSTATUS(_Screen,"System",5)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx")))
ENDIF
.oGDIGraphics = _SCREEN.SYSTEM.Drawing.Graphics.New()
.SetFRXDataSession()
DIMENSION .aRecords[reccount(), 12]
.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,;
nObjectContinuationTyENDFUNC
PROCEDURE RENDER(tnFRXRecNo,;
tnLeft,tnTop,tnWidth,tnHeight,;
nObjectContinuationType, ;
cContentsToBeRendered, GDIPlusImage)
LOCAL lcText
lcText = This.aRecords(tnFRXRecNo,1)
IF VARTYPE(lcText) = "C" AND lcText = "<FJ>"
lcText = SUBSTR(lcText,5) && Quitar la etiqueta <FJ> de la cadena
This.oGDIGraphics.Handle = This.GDIPlusGraphics
WITH _SCREEN.SYSTEM.Drawing
* Crear un objeto GDI+ Rectangle que especifique
* el área donde vamos a dibujar el texto.
LOCAL loRectF as xfcRectangleF
loRectF = .RectangleF.New(tnLeft, tnTop, tnWidth, tnHeight)
* Crear un objeto Font basado en la configuración original del informe
LOCAL loFont as xfcFont
loFont = .Font.New(This.aRecords(tnFRXRecNo,2) ;
, This.aRecords(tnFRXRecNo,4), This.aRecords(tnFRXRecNo,3) ;
, .GraphicsUnit.Point)
* Si tenemos un fondo opaco para el texto,
* entonces dibujar un rectángulo utilizando el fondo escogido
IF This.aRecords[tnFRXRecno,8] <> 0 && Alpha
* Obtener los colores de fondo
LOCAL lnRed, lnGreen, lnBlue, lnAlpha
lnRed = This.aRecords[tnFRXRecno,5]
lnGreen = This.aRecords[tnFRXRecno,6]
lnBlue = This.aLOCAL loBackBrush as xfcSolidBrush
loBackBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
* Dibujar el rectángulo de fondo
This.oGDIGraphics.FillRectangle(loBackBrush, tnLeft, tnTop, tnWidth, tnHeight)
ENDIF
* Obtener los colores para el texto
lnRed = This.aRecords[tnFRXRecno,9]
lnGreen = This.aRecords[tnFRXRecno,10]
lnBlue = This.aRecords[tnFRXRecno,11]
lnAlpha = This.aRecords[tnFRXRecno,12]
* Crear una brocha sólida que se utilizará para dibujar el texto
LOCAL loTextBrush as xfcSolidBrush
loTextBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
* Finalmente, dibujar el texto en modo FullJustified.
This.oGDIGraphics.DrawStringJustified(lcText, loFont, loTextBrush, loRectF)
ENDWITH
ELSE
* Si no dibujamos una cadena completamente justificada,
* dejamos que VFP dibuje el texto como siempre.
DODEFAULT(tnFRXRecNo, tnLeft, tnTop, tnWidth, tnHeight, ;
nObjectContinuationType, cContentsToBeRendered, GDIPlusImage)
ENDIF
* Como dibujamos el texto, no deseamos
* que ocurra el comportamiento predeterminado.
NODEFAULT
ENDPROC
FUNCTION EvaluateContents(tnFRXRecNo, toObjProperties)
* Obtener los datos del FRX
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
Esto es muy fácil de utilizar
Abra un informe, y seleccione un cuadro de texto que tenga asociado un campo memo. En el campo propiedades, seleccione la ficha General, en "Expression" añada la cadena "<fj>" antes del campo original. Suponiendo que usted tiene la expresión "MiTabla.MiCampoMemo", para poderlo justificar, cámbielo por: "<fj>"+MiTabla.MiCampoMemo.</fj></fj>
El siguiente paso es asegurarse de que VFP utilizará nuestro listener para generar el informe.
* Decir a VFP que utilizaremos las nuevas características de informes
SET REPORTBEHAVIOR 90
LOCAL loReportListener
loReportListener = NEWOBJECT("FullJustifyListener",LOCFILE("FJListener.prg"))
* loReportListener = CREATEOBJECT("FullJustifyListener")
loReportListener.LISTENERTYPE = 1
REPORT FORM MyReport OBJECT loReportListener

ACTUALIZADO 07/04/2007
Después de algunas sugerencias de Victor Espinoza, debajo está la versión modificada de la clase "FJListener", que va a obtener la etiqueta <FJ> del campo user data del cuadro de texto (que está debajo de la ficha "Otros"), de esta forma la etiqueta "<FJ>" no afectará al informe si más tarde se decide utilizar un report listener alternativo.

* Programa : FJLISTENER.PRG
* * Objetivo : Obtener un Report Listener que permita generar textos
* con alineación totalmente justificada.
* Autores : Cesar Chalom y Victor Espinoza
* La clase está basada en el artículo "Listening to a report" por Doug Hennig
* http://msdn2.microsoft.com/en-us/library/ms947682.aspx
DEFINE CLASS FullJustifyListener AS _ReportListener OF ;
ADDBS(HOME(1)) + "FFC\_ReportListener.VCX"
oGDIGraphics = NULL
DIMENSION aRecords[1]
* Doug Hennig - Si no está configurado aun la propiedad ListenerType,
* configurar basado en si el informe será previsualizado o impreso.
FUNCTION LoadReport
WITH This
DO CASE
CASE .ListenerType <> -1
CASE .CommandClauses.Preview
.ListenerType = 1
CASE .CommandClauses.OutputTo = 1
.ListenerType = 0
ENDCASE
ENDWITH
DODEFAULT()
ENDFUNC
* Antes de ejecutar el informe, necesitamos estar seguros de que fue inicializado GdiPlusX
* y crear un nuevo objeto Graphics
FUNCTION BEFOREREPORT
DODEFAULT()
WITH This
* Verificar si ya tenemos el objeto "System" en "_Screen"
IF NOT PEMSTATUS(_Screen,"System",5)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", LOCFILE("system.vcx")))
ENDIF
.oGDIGraphics = _SCREEN.SYSTEM.Drawing.Graphics.New()
.SetFRXDataSession() && activa la sesión de datos del cursor FRX
DIMENSION .aRecords(RECCOUNT(), 13)
SCAN FOR "<FJ>" $ UPPER(User)
.aRecords(RECNO(), 13) = "FJ"
ENDSCAN
.ResetDataSession() && restablece datasession ID a la sesión de datos donde está el listener
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)
IF VARTYPE(This.aRecords(tnFRXRecNo,13)) = "C" AND ;
This.aRecords(tnFRXRecNo,13) == "FJ"
LOCAL lcText
lcText = This.aRecords(tnFRXRecNo,1)
This.oGDIGraphics.Handle = This.GDIPlusGraphics
WITH _SCREEN.SYSTEM.Drawing
* Crear un objeto GDI+ Rectangle que especifique
* el área donde vamos a dibujar el texto.
LOCAL loRectF as xfcRectangleF
loRectF = .RectangleF.New(tnLeft, tnTop, tnWidth, tnHeight)
* Crear un objeto Font basado en la configuración original del informe
LOCAL loFont as xfcFont
loFont = .Font.New(This.aRecords(tnFRXRecNo,2) ;
, This.aRecords(tnFRXRecNo,4), This.aRecords(tnFRXRecNo,3) ;
, .GraphicsUnit.Point)
* Si tenemos un fondo opaco para el texto,
* entonces dibujar un rectángulo utilizando el fondo escogido
IF This.aRecords[tnFRXRecno,8] <> 0 && Alpha
* Obtener los colores de fondo
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]
* Crear una brocha sólida que se utilizará para dibujar el fondo
LOCAL loBackBrush as xfcSolidBrush
loBackBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
* Dibujar el rectángulo de fondo
This.oGDIGraphics.FillRectangle(loBackBrush, tnLeft, tnTop, tnWidth, tnHeight)
ENDIF
* Obtener los colores del texto
lnRed = This.aRecords[tnFRXRecno,9]
lnGreen = This.aRecords[tnFRXRecno,10]
lnBlue = This.aRecords[tnFRXRecno,11]
lnAlpha = This.aRecords[tnFRXRecno,12]
* Crear una brocha sólida que utilizaremos para dibujar el texto
LOCAL loTextBrush as xfcSolidBrush
loTextBrush = .SolidBrush.New(;
.Color.FromArgb(lnAlpha,lnRed, lnGreen, lnBlue))
* Finalmente, dibujar el texto en modo FullJustified.
This.oGDIGraphics.DrawStringJustified(lcText, loFont, loTextBrush, loRectF)
ENDWITH
ELSE
* Si no dibujamos una cadena completamente justificada,
* dejamos que VFP dibuje el texto como siempre..
DODEFAULT(tnFRXRecNo, tnLeft, tnTop, tnWidth, tnHeight, ;
nObjectContinuationType, cContentsToBeRendered, GDIPlusImage)
ENDIF
* Como dibujamos el texto, no deseamos
* que ocurra el comportamiento predeterminado.
NODEFAULT
ENDPROC
FUNCTION EvaluateContents(tnFRXRecNo, toObjProperties)
* Obtener los datos del FRX
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
Por ahora esto me parece bien. Pero existen muchas cosas que se deben mejorar. Para la siguiente versión pudiera ser una directiva que se utilice junto con los otros report listeners, como hizo Doug Hennig en su gran artículo "Listening to a Report".

Enlaces relacionados que me ayudaron a entender cómo trabaja el Report Listener:
Nota de la traductora: Estos artículos están traducidos y publicados en PortalFox
|