26 de octubre de 2005

Convertir tablas a formato HTML


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 [&nbsp;]
      lcTexto = STRTRAN(lcTexto, [ ], [&nbsp;])
    ENDIF
    RETURN lcTexto
  ENDPROC
  *--
  PROCEDURE TransformarTexto(tcTexto)
    *-- Transformo algunos caracteres "\<>&
    tcTexto = STRTRAN(tcTexto, ["], [&quot;])
    tcTexto = STRTRAN(tcTexto, [\], [&#092;])
    tcTexto = STRTRAN(tcTexto, [<], [&lt;])
    tcTexto = STRTRAN(tcTexto, [>], [&gt;])
    tcTexto = STRTRAN(tcTexto, [&], [&amp;])
    *-- Si es vacio o nulo
    IF EMPTY(tcTexto) OR ISNULL(tcTexto)
      tcTexto = [&nbsp;]
    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

2 comentarios :

  1. El procedimiento de transformar el texto está erróneo. El correcto es el abajo indicado.
    PROCEDURE TransformarTexto(tcTexto)
    *-- Transformo algunos caracteres "\<>&
    tcTexto = STRTRAN(tcTexto, ["], ["])
    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

    ResponderBorrar

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.