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:
? _GENHTMLEsta 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 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", 2Hasta 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?
Hasta la próxima.
Luis María Guayán