Introducción
En repetidas ocasiones he necesitado pasar mis tablas o cursores resultantes de una consulta a otro formato para enviar información a los usuarios. Obviamente el formato mas conocido es el texto plano y con un simple
COPY TO MiArchivo.txt TYPE SDF ya tengo el archivo generado y listo para entregar, pero este formato seguramente no es de nuestro agrado (... y mucho menos del agrado de nuestros usuarios o superiores). En este artículo vamos a ver como podemos pasar nuestras tablas a formato
HTML (
Hyper Text Mark-up Language) y así poder darle una mejor apariencia, para enviarlo como un correo electrónico o para publicarlo en un sitio de Internet.
¿Qué hay de nuevo viejo?
Aunque muchos lo desconozcan, Visual FoxPro desde hace ya varias versiones anteriores trae una herramienta para poder convertir tablas, formularios, informes, etiquetas y menús a formato HTML. La herramienta se llama
GenHtml.prg y se encuentra en la carpeta donde está instalado VFP. La ruta y nombre de este programa está almacenado en la variable del sistema
_GENHTML.
Comprobamos que la variable del sistema este correctamente configurada con:
? _GENHTML
Esta variable del sistema la podemos configurar desde el menú de VFP, en Herramientas, Opciones, en la solapa Archivos, y buscamos en la lista "Generador de HTML".
El programa GenHtml.prg
Seguramente muchos utilizaron este programa aun sin saberlo, ya que se llega desde el menú de VFP al seleccionar Archivo, Guardar como HTML ..., cuando nos encontramos en el diseñador de formularios, el diseñador de etiquetas, el diseñador de menús, el diseñador de informes, o en una ventana Examinar (Browse) de una tabla o cursor.
GenHtml.prg utiliza la librería de clases visuales
_Html.vcx que son parte de las
FoxPro Foundation Classes (FFC) que vienen con Visual FoxPro y se encuentran en la carpeta \ffc\ del directorio de instalación de VFP. ¿Cómo ...? ¿Tampoco conocen las FoxPro Foundation Classes?
En la aplicación de ejemplo
Solution que viene con VFP (esta aplicación si que la conocen por el artículo de Esparta Palma "
Y tú has explorado el ejemplo Solution.app?"), viene un formulario de ejemplo para generar código HTML a partir de tablas DBFs, bajo la rama Foundations Classes, y Generate HTML. Podemos ejecutar directamente el formulario con la siguiente sentencia:
DO FORM (HOME(2) + "Solution\Ffc\DoHtml.scx")
Usar GenHtml.prg
Desde la ventana de comandos o desde una aplicación podemos usar directamente el programa GenHtml.prg pasándole los parámetros correspondientes. Algunos de estos parámetros son:
- El archivo HTML de destino.
- El archivo de origen, que puede ser de extensión DBF (tabla), SCX (formulario), FRX (informe), LBX (etiquetas) o MNX (menús)
- La opción de visualización, que algunos de sus posibles valores son: 0=Genera el archivo HTML; 1=Genera y muestra el archivo HTML en el editor de VFP; 2=Genera y muestra el archivo HTML en el Explorador de Internet y 3=Genera el archivo HTML y muestra el cuadro de diálogo "Guardar como HTML" con las opciones anteriores.
Para conocer la sintaxis completa y la lista de todos los parámetros, lean la ayuda de VFP en el tema GenHtml.prg.
Para ver un ejemplo de como se genera un archivo HTML a partir de la tabla Products de la base de datos Northwind, ejecutamos la siguiente sentencia:
DO (_GENHTML) WITH "Productos.htm", HOME(2)+"Northwind\Products.dbf", 2
Hasta aquí vimos que podemos generar archivos HTML con las herramientas que nos ofrece Visual FoxPro. El código del programa y las clases de las FFC las podemos incluir sin inconvenientes en nuestras aplicaciones.
Otra clase mas
Personalmente tuve la necesidad de crear una clase mas elemental y sencilla, que solo convierte tablas y cursores a formato HTML. Aquí la expongo para que ustedes la utilicen en sus aplicaciones y quizás necesiten modificarla para agregarle mas datos al archivo HTML o para cambiar la apariencia de visualización.
La definición de la clase y la forma de uso están en en siguiente código:
*-- Ejemplo de uso
USE (HOME(2) + "Northwind\Products")
loHtml = CREATEOBJECT("ClaseHtml")
IF loHtml.Tabla2Html("Products","C:\Listado.htm") > 0
*-- Se generó el archivo HTML y lo visualizo
*-- con la aplicación asociada
loShell = CREATEOBJECT("Shell.Application")
loShell.ShellExecute("C:\Listado.htm")
loShel = NULL
RELEASE loShell
ELSE
MESSAGEBOX("No se pudo generar el archivo HTML",16,"Error")
ENDIF
loHtml = NULL
RELEASE loHtml
*--------------------------------
DEFINE CLASS ClaseHtml AS CUSTOM
*--
*-- Clase para convertir una Tabla a formato HTML
*--
#DEFINE CR CHR(13)
#DEFINE LF CHR(10)
#DEFINE CR_LF CR + LF
*-- Propiedades
DIMENSION aCampos(1,1)
nCampos = 0
nRegistros = 0
cNombreAlias = ""
cArchivoHtml = ""
cHtml = ""
cColorBody = "#DDDDDD"
cColorTitle = "#FFFFDD"
cColorTable = "#FFFFFF"
*--
PROCEDURE Tabla2Html(tcNombreAlias, tcArchivoHtml)
LOCAL lnErr
IF EMPTY(tcNombreAlias) OR NOT USED(tcNombreAlias)
RETURN -1
ENDIF
IF EMPTY(tcArchivoHtml)
tcArchivoHtml = FORCEEXT(tcNombreAlias,"htm")
ENDIF
THIS.cNombreAlias = tcNombreAlias
THIS.cArchivoHtml = tcArchivoHtml
THIS.nCampos = AFIELDS(THIS.aCampos,THIS.cNombreAlias)
THIS.EscribirHtml()
lnErr = THIS.GuardarArchivo()
RETURN lnErr
ENDPROC
*--
PROCEDURE EscribirHtml()
*-- Inicio html
THIS.cHtml = THIS.IniciarTag([html]) + CR_LF
*-- Head
THIS.cHtml = THIS.cHtml + THIS.IniciarTag([head]) + CR_LF
THIS.cHtml = THIS.cHtml + THIS.AgregarTags(THIS.cNombreAlias,[title]) + CR_LF
THIS.cHtml = THIS.cHtml + THIS.TerminarTag([head]) + CR_LF
*-- Inicio Body
THIS.cHtml = THIS.cHtml + THIS.IniciarTag([body],[bgcolor="] + ;
THIS.cColorBody + ["]) + CR_LF
*-- Titulo
THIS.cHtml = THIS.cHtml + THIS.AgregarTags(PROPER(THIS.cNombreAlias),[h3]) + CR_LF
*-- Tabla
THIS.cHtml = THIS.cHtml + THIS.AgregarTabla(THIS.cNombreAlias) + CR_LF
*-- Cantidad de registros
THIS.cHtml = THIS.cHtml + THIS.AgregarTags(TRANSFORM(THIS.nRegistros) + ;
[ registros.], [p]) + CR_LF
*-- Fin Body
THIS.cHtml = THIS.cHtml + THIS.TerminarTag([body]) + CR_LF
*-- Fin html
THIS.cHtml = THIS.cHtml + THIS.TerminarTag([html]) + CR_LF
ENDPROC
*--
PROCEDURE AgregarTags(tcTexto, tcTag, tcOpciones)
RETURN [<] + LOWER(tcTag) + IIF(EMPTY(tcOpciones),[],[ ] + tcOpciones) + [>] + ;
ALLTRIM(tcTexto) + [</] + LOWER(tcTag) + [>]
ENDPROC
*--
PROCEDURE IniciarTag(tcTag, tcOpciones)
RETURN [<] + LOWER(tcTag) + ;
IIF(EMPTY(tcOpciones),[],[ ] + tcOpciones) + [>]
ENDPROC
*--
PROCEDURE TerminarTag(tcTag)
RETURN [</] + LOWER(tcTag) + [>]
ENDPROC
*--
PROCEDURE AgregarTabla(tcNombreAlias)
LOCAL lcTexto, ln
lcTexto = THIS.IniciarTag([table],[border="1" cellpadding="2" cellspacing="0"] + ;
IIF(EMPTY(THIS.cColorTable),[],[ bgcolor="] + THIS.cColorTable + ["])) + CR_LF
*-- Fila cabecera
lcTexto = lcTexto + THIS.IniciarTag([tr], ;
IIF(EMPTY(THIS.cColorTitle),[],[bgcolor="] + THIS.cColorTitle + ["])) + CR_LF
*-- Recorro campos
FOR ln = 1 TO THIS.nCampos
lcTexto = lcTexto + THIS.AgregarTags(THIS.aCampos(ln,1),[th]) + CR_LF
ENDFOR
lcTexto = lcTexto + THIS.TerminarTag([tr]) + CR_LF
*-- Filas de registros
SELECT (tcNombreAlias)
SCAN ALL
THIS.nRegistros = THIS.nRegistros + 1
*-- Inicio fila
lcTexto = lcTexto + THIS.IniciarTag([tr]) + CR_LF
*-- Recorro los campos
FOR ln = 1 TO THIS.nCampos
lcTexto = lcTexto + THIS.AgregarTags(THIS.TomarValorCampo(ln),[td], ;
THIS.AlinearSoloNumeros(ln)) + CR_LF
ENDFOR
*-- Fin fila
lcTexto = lcTexto + THIS.TerminarTag([tr]) + CR_LF
ENDSCAN
lcTexto = lcTexto + THIS.TerminarTag([table])
RETURN lcTexto
ENDPROC
*--
PROCEDURE TomarValorCampo(tn)
LOCAL lcTexto, lcTipo
*-- Tipo de campo
lcTipo = THIS.aCampos(tn,2)
DO CASE
CASE INLIST(lcTipo,"C","M") && Caracter y Memo
lcTexto = ALLTRIM(EVALUATE(THIS.aCampos(tn,1)))
CASE INLIST(lcTipo,"D","T","I","L")
lcTexto = TRANSFORM(EVALUATE(THIS.aCampos(tn,1)))
CASE INLIST(lcTipo,"Y") && Monetario
*-- Quitar esta opción si no se desar el símbolo monetario
*-- Este tipo también está contemplado en la opcion siguiente
lcTexto = TRANSFORM(EVALUATE(THIS.aCampos(tn,1)))
CASE INLIST(lcTipo,"N","F","B","Y")
IF THIS.aCampos(tn,4) = 0
lcTexto = ALLTRIM(STR(EVALUATE(THIS.aCampos(tn,1))))
ELSE
lcTexto = ALLTRIM(STR(EVALUATE(THIS.aCampos(tn,1)), ;
THIS.aCampos(tn,3) + THIS.aCampos(tn,4) + 1, THIS.aCampos(tn,4)))
ENDIF
CASE INLIST(lcTipo,"G") && General
lcTexto = [gen]
CASE INLIST(lcTipo,"Q","V","W") && Nuevos tipos de VFP 9.0
lcTexto = TRANSFORM(EVALUATE(THIS.aCampos(tn,1)))
OTHERWISE
lcTexto = ""
ENDCASE
lcTexto = THIS.TransformarTexto(lcTexto)
IF lcTipo = "M" && Si es campo Memo
*-- Transformo retornos de carro y saltos de lineas
lcTexto = STRTRAN(lcTexto, CR_LF, [<br>])
lcTexto = STRTRAN(lcTexto, CR, [<br>])
lcTexto = STRTRAN(lcTexto, LF, [<br>])
ELSE && Si no es campo Memo
*-- Transformo espacios [ ] por [ ]
lcTexto = STRTRAN(lcTexto, [ ], [ ])
ENDIF
RETURN lcTexto
ENDPROC
*--
PROCEDURE TransformarTexto(tcTexto)
*-- Transformo algunos caracteres "\<>&
tcTexto = STRTRAN(tcTexto, ["], ["])
tcTexto = STRTRAN(tcTexto, [\], [\])
tcTexto = STRTRAN(tcTexto, [<], [<])
tcTexto = STRTRAN(tcTexto, [>], [>])
tcTexto = STRTRAN(tcTexto, [&], [&])
*-- Si es vacio o nulo
IF EMPTY(tcTexto) OR ISNULL(tcTexto)
tcTexto = [ ]
ENDIF
RETURN tcTexto
ENDPROC
*--
PROCEDURE AlinearSoloNumeros(tn)
LOCAL lcTexto, lcTipo
lcTipo = THIS.aCampos(tn,2)
DO CASE
CASE INLIST(lcTipo,"N","F","B","Y","I")
*-- Alinea a la derecha solo campos numéricos
lcTexto = [align="right"]
OTHERWISE
lcTexto = []
ENDCASE
RETURN lcTexto
ENDPROC
*--
PROCEDURE GuardarArchivo()
RETURN STRTOFILE(THIS.cHtml,THIS.cArchivoHtml)
ENDPROC
*--
ENDDEFINE
*--------------------------------
Conclusiones
Las conclusiones que podemos sacar de este artículo son muchas y dependen de las respuestas a las siguientes preguntas:
- ¿Conocían el programa GenHtml.prg?
- ¿Generaron alguna vez archivos HTML desde el menú Archivo, Guardar como HTML?
- ¿Utilizaron alguna vez las FoxPro Foundation Classes?
- ¿Exploraron la aplicación Solution.app o el formulario DoHtml.scx?
- ¿Desarrollaron algún código para generar archivos HTML?
Si contestaron mas de un "
NO", los invito a que exploren los ejemplos y las herramientas que nos brinda Visual FoxPro, que son muchas mas de las que se imaginan, y nos brindan la posibilidad de dar soluciones rápidas y efectivas a los requerimientos de nuestros usuarios.
Hasta la próxima.
Luis María Guayán