Jan 08 2009

Programación chapucera

Supongo habran sabido que los zunes se suicidaron el pasado 31 de diciembre. Un zune es un aparato de hasecorp medianamente parecido a iPod. Al grano, resulta ser que una validación mal hecha en la función de calculo de la fecha tenia la condición if (days > 366) para darse cuenta de los años bisiestos pero deberia ser if (days >= 366), es decir, mayor o igual. Como la comparación esta mal y solo pregunta por mayores entonces el día 366 de un año bisiesto no entra a la condición, que desafortunadamente esta puesta dentro de un ciclo así que el zune se quedo ahí de por vida el 31 de diciembre. Les dejo el fragmento de código fuente.

while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
else
{
days -= 365;
year += 1;
}
}
}

Pueden ver el código fuente completo aquí

El caso de esto es una muestra de que un pequeño descuido en la programación puede crear problemas futuros inimaginables bajo quien sabe que circunstancias. En mi trabajo ultimamente me dedico más al analisis y desarrollo de aplicaciones nuevas así cuando es necesario dar mantenimiento o agregar funcionalidades simples a mis aplicaciones ya terminadas suelo recurrir a un programador cualquiera del centro para que el haga la talacha aburrida. Por un lado es comodo, por otro es un volado. Esta semana estuvimos a punto de meternos en un broncon por una falla en la programación casi tan simple como ese del mencionado zune, una consulta dirigida en dirección equivocada y voila, iba a sobreescribir información y por más suerte que merito encontre el bug antes de que lo pusieran a trabajar en el ambiente final, y eso que la aplicación ya la habia probado el área de beta testers, según.

El caso es que me ha tocado ver de manera relativamente frecuente fallos de programación así y por lo regular la causa es el uso de programadores que no rinden cuentas directamente si no que “la bronca es del lider” y por lo tanto salen las cosas como sea nada más para decir que se hizo pero el resultado es que las posibles excepciones o situaciones extrañas no estan validadas ni probadas y por eso pasa lo que pasa. El estar apurando los tiempos de entraga y dejar que las pruebas sean un mero requisito hecho por decir que se hizo no coopera, cuanta razón tienes Fuckwoski.

Me acorde del ultimo juego de Tomb Raider, muchos errores de programación sin testear.

Dec 19 2008

Programando un WebBrowser multiplataforma

Hace un tiempo les comente de una aplicación con un ActiveX WebBrowser basado en Gecko y Mozilla. Esta aplicación esta hecha en .NET (NetFramework de Microsoft) con Windows Forms y el control principal, pues es un ActiveX. Obviamente esta aplicación es para Windows, pero por ciertas necesidades que aquí no vienen a cuento pero requieren que el explorador quede abierto después de una manipulación inicial al contenido de la página mostrada, se requirio un navegador así pero en Linux, entonces ¿Podria hacer una aplicación multiplataforma en lugar de dos especializadas? Me aventure a intentarlo. Aclaro desde ahora que aquí solo incluyo un ejemplo simple de como montar crear un explorador basico y nada más.

Empece por contemplar posibilidades para hacerlo. Hay algunas como por ejemplo WebControl con Mono 2.0 porque en esta versión se incluyo en el framework un control basado en Mozilla a modo de emular la manera en que el NetFramework de Microsoft puede manejar el motor de Internet Explorer para incluir navegación web en controles windows forms. Mono 2.0 incluye soporte para windows forms permitiendo usar aplicaciones windows como multiplataforma.

Del caso anterior no me gusto mucho el uso de Windows forms porque después de todo en mi caso particular mi prioridad es que funcione en linux me parecio mejor algo con GTK así que escogí probar a incluir un control de Gecko en una ventana GTK. Para esto use el control Gecko#. Hay un control similar llamado GtkMozEmbed que también permite incluir browsers en aplicaciones GTK pero me parecio que esta algo verde, aunque no la probe solo lei la documentación de su página y reconocen que esta limitada. No encontre como manejar el DOM, solo cargar páginas.

También existe en desarrollo un control para incluir WebKit en ventanas GTK#. WebKit también es un motor de navegadores web y es usado por Safari y puede ser incluido en las aplicaciones .NET consiguiendolo para agregarlo como referencia en la aplicación. El componente se llama webkit-sharp para quien quiera profundizar en sus posibilidades. Esta opción la encontre cuando ya tenia un poco avanzado el desarrollo y por eso no indague más pero me parece bueno mencionarla también.

AMBIENTACIÓN Y REQUERIMIENTOS

Desarrollo esta aplicación usando SharpDevelop (2.2.1), compilando con Mono 2.0 y creando el entorno grafico con GTK#; todo sobre Windows XP en español, el sistema operativo que instalaron en mi trabajo. Extraño el diseñador de ventanas Stetic que trae MonoDevelop pero ya que, si lo necesito en mi casa tengo Ubuntu con MonoDevelop y ambiente grafico Gnome que me servira para pruebas complementarias de compatibilidad y también dispongo de una computadora con la distribución slackware con el ambiente grafico KDE 3.4 donde también probare el desarrollo. Uhm, me falta un Mac… ademas no me atrae nada de nada seguirle con esto en casa, salvo la curiosidad de ver si funciona o no, cosa que no me llevará mucho tiempo.

Dando por hecho que es obvio que hay que tener instalado MONO (estoy usando el 2.0.1) y de preferencia un IDE como MonoDevelop (GNU/Linux) o Sharp Develop (Windows) para manejarlo, aunque te las podrias arreglar con un editor de texto y compilando por linea de comandos si te gusta “lo retro“, te gusta presumir que programas “como los hombres” o si por alguna razón particular no hay una mejor opción en tu caso.

Para poder compilar adecuadamente con SharpDevelop y Mono en Windows XP en español tuve que hacer unos ajustes a las variables de entorno de Windows XP. Hay que crear la variable MONO_EXTERNAL_ENCODINGS para que mono trabaje perfectamente con una PC que no tenga el sistema operativo por default en ingles. si, ya se, que chafa, decia, es así:

Ah, no faltara quien no sepa donde y como poner las variables así que… Click derecho a Mi PC -> Propiedades -> Pestaña Opciones Avanzadas -> Variables de entorno. Con eso les aparecera la ventana donde poner variables de entorno para tu usuario y globales para todos. Si una misma variable existe en ambos, se le da prioridad a la del usuario sobre la global. La global solo se toma si en tu usuario no existe. Toma en cuenta esto. En mi caso declare a MONO_EXTERNAL_ENCODINGS como global.

Variables de entorno

Variables de entorno

Todavia no cierres la ventana, aun hay unas variables de entorno que necesite modificar. Primero la variable Path, donde agregue la dirección del directorio bin de mi instalación de MONO. Las otras variables de entorno que modifique son TEMP y TMP. Estas ultimas indican donde Windows pondra y buscará los archivos temporales que valla creando. En mi caso lo cambie porque en las variables de entorno de mi usuario, al estar el sistema operativo en español, me dejaba los archivos temporales en C:\Documents and Settings\adan\Configuración local\Temp. Esto es un problema por los acentos y espacios en la ruta por lo mismo de que al parecer no validaron que hay programadores fuera de los paises de habla inglesa, y que provoca un error al compilar (creo que era el CS2011) proyectos GLADE# y GTK# (por lo menos en esos me dio problema a mi, tal vez halla más) así que le di la misma dirección que existia en las variables de entorno globales.

Entonces las variables de entorno me modificadas por mi quedan así:

MONO_EXTERNAL_ENCODINGS=default_locale
Path=C:\Archivos de programa\Mono-2.0.1\bin
TEMP=%SystemRoot%\TEMP
TMP=%SystemRoot%\TEMP

Instalando Gecko#

Vamos por partes como el descuartizador, primero necesitamos el Gecko Runtime Enviroment, o GRE para los amigos. Esto nos provee de las librerias necesarias para incluir el motor de navegación de Gecko en nuestras aplicaciones. En Linux esto ya es parte de la distribución en casos que ya traen exploradores basados en Mozilla desde el principio. En Windows se necesita instalar un GRE especial porque ademas de que no esta y al parecer el oficial tiene un problema con pasarle cadenas en UTF8 a Gecko#

Yo use el instalador que se encuentra en la página de Novell, GRE-GeckoSharp-1.7.12-0.1.exe. Si todo salio bien tendran también una nueva variable de entorno que dirá GECKOSHILLA_BASEPATH=C:\Archivos de programa\Archivos comunes\gtkmozembed-win32 y también también debe estar en el Mono Global Assembly Cache. Busquen la dll en la carpeta GAC de la instalación de MONO en Archivos de programa. Esta variable de entorno es requerida en Windows para que la aplicación encuentre la libreria gtkembedmoz.dll, necesaria para usar el WebControl.

Agregando DLL al GAC de MONO

Puedes agregar la dll de Gecko# al GAC de MONO desde linea de comandos con el comando gacutil -i gecko-sharp.dll desde la carpeta donde quedo instalado originalmente. Algo así como en la imagen mostrada arriba.

Ahora si, ya podemos poner el Gecko# en nuestras aplicaciones. Cree un proyecto que usa GTK# para la creación de ventanas y agregue la referencia a la .dll de Gecko# para poder usar el Gecko.WebControl en nuestro código.

agregar referencia

Agregando referencia a Gecko# en nuestro proyecto

Ocurrio un extraño problema que lo deja a uno con cara de WTF? Resulta que a la hora de compilar la aplicación con la dll de Gecko# agregada como referencia el compilador arroja lo siguiente:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets(0,0) : Advertencia MSB3247: Found conflicts between different versions of the same dependent assembly.

¿Pero que rayos? Estoy compilando con MONO ¿Por qué se mete el NetFramework aquí? Solo he instalado una vez el Gecko#, no veo porque deberia tener versiones diferentes regadas por la PC. Basta de preguntas, voy por soluciones. Voy a buscar documentación en internet un rato.

Ok. Al parecer no hay manera de usar MONO al ejecutar el debugger en SharpDevelop, solo con el NetFramework de Microsoft. Para poder usar a MONO y que el .NET de Hasecorp no me tire errores lo que he hecho es lo siguiente.

Primero seleccionar a Mono 2.0 como mi framework indicado. Para eso edito las propiedades del proyecto como se ve a continuación.

Usando el Framework de MONO

Seleccionando MONO 2.0 como mi framework a usar

Con eso usara el Gmcs (o Mcs con MONO 1.1) para correr la aplicación después de compilarla. Pero si uso las opciones de debug sigue recurriendo al de Microsoft y por eso me marca el error antes mencionado arriba. Para poder correr la aplicación con el Framework debo preparar los parametros de linea de comando de esta manera, como muestro en la imagen siguiente hay que indicar que se va a iniciar con un programa externo (MONO) así que indico la ruta donde esta en la instalación y también un par de parametros por linea de comando ${TargetPath} y ${TargetDir}

Preparando para correr con MONO

Preparando el proyecto para correrlo con MONO

Pero como mencione antes, si tratas de usar debugger entonces SharpDevelop recurre al Framework de Microsoft, el cual lanza excepciones raras, así que uso la opción Ejecutar sin depurador o Run without debugger para los que lo tengan en ingles. De este modo no pasará por el debugger y generará el ejecutable compilandolo con MONO, pero siempre que uses el debbuger NetFramework seguira marcando el mismo problema antes mencionado.

Hecho todo esto, ya estoy listo para programar.

DESARROLLO DE LA APLICACIÓN

Me puse a codificar para crear un explorador basico de prueba, muestro el código fuente a continuación. Es solo un explorador sencillo que ejecuto con MONO y enviandole la dirección URL de la página que desee abrir y nada más. En esto han venido a resumirse las horas de documentación y desarrollo en las que he trabajado ultimamente.

using Gtk;
using System;
using Gecko;
namespace GeckoBrowser
{
/// Ejemplo sencillo de explorador web basado en Gecko#
public class MainWindow : Window
{
#region Variables de la clase
/// Explorador web incluido
WebControl web;
/// Direccion de la pagina cargada en el explorador
static string UrlPagina;
#endregion
#region Punto de entrada de la aplicacion
[STAThread]
public static void Main(string[] arg)
{
Application.Init();
if (arg.Length > 0)
{
UrlPagina = arg[0]; // Obtener la pagina indicada por parametros
}
new MainWindow();
Application.Run();
}
#endregion
#region Ventana y sus eventos
public MainWindow() : base(“MainWindow”)
{
DeleteEvent += new DeleteEventHandler(MainWindowDeleteEvent);
#region Codigo requerido por GRE and Gecko# en Windows
string mozillaEnvPath = System.Environment.GetEnvironmentVariable(“GECKOSHILLA_BASEPATH”);
if (mozillaEnvPath != null && mozillaEnvPath.Length != 0)
{
Gecko.WebControl.CompPath = mozillaEnvPath;
}
#endregion
web = new WebControl(); // Inicializar el explorador
web.LoadUrl(UrlPagina); // Abrir la pagina indicada
this.Add(web); // Agregar el control a la ventana
ShowAll();
}
void MainWindowDeleteEvent(object o, DeleteEventArgs args)
{
Application.Quit();
args.RetVal = true;
}
#endregion
}
}

Para que este código funcione he agregado como referencia en mi proyecto las siguientes DLL que he usado y no estan incluidas en NetFramework. Yo las tengo por MONO y por el mismo GTK#

  • atk-sharp
  • gdk-sharp
  • gecko-sharp
  • glib-sharp
  • gtk-sharp
  • pango-sharp

Ademas de estas dll, también pongo en la carpeta junto al ejecutable a gtkembedmoz.dll debido a que cuando lo probe en GNU/Linux (un Ubuntu 8.10 con Gnome para más señas) me arrojo un mensaje diciendo que me faltaba ese archivo. Esta dll la tome de la carpeta donde se instala GTK# como explique al principio de la nota. En linux puedes tomarla de la instalación de Mozilla o FireFox que seguramente estará en /usr/lib/[carpeta de tu navegador] por lo regular.

Navegador

Así se ve el navegador

La aplicación me funciono bien excepto en un caso, cuando la ejecute para abrir una página segura me solicito la instalación del Personal Security Manager (PSM) como requisito para mostrar las páginas HTTPS, con las demas sin problema. Esto es cosa del navegador, Gecko en este caso. Este paquete consiste en un set de librerias para operaciones Cryptograficas, incluida SSL por lo que es necesaria para visualizar “páginas seguras” (https) y manejo de certificados. Al parecer los navegadores como Mozilla FireFox incluyen este componente pero lo hacen en si mismos, en una capa por encima de Gecko así que por eso no lo estoy usando de manera predeterminada.

En GNU/linux me dio una excepción debida a falta de ambientación. El mensaje es este:

Unhandled Exception: System.TypeInitializationException: An exception was thrown by the type initializer for Gecko.WebControl —>
System.DllNotFoundException: gtkembedmoz.dll
at (wrapper managed-to-native) Gecko.WebControl:gtk_moz_embed_get_type ()
at Gecko.WebControl.get_GType () [0x00000]
at GtkSharp.GeckoSharp.ObjectManager.Initialize () [0x00000]
at Gecko.WebControl..cctor () [0x00000] — End of inner exception stack trace —
at GeckoBrowser.MainWindow..ctor () [0x00000]
at GeckoBrowser.MainWindow.Main (System.String[] arg) [0x00000]

Entonces abri el archivo

/etc/environment para agregar la variable LD_LIBRARY_PATH=”[inserte aquí la ruta de los .so de mozilla, firefox o donde sea que este]” y ya con eso funciona.

Explorador solicitando Personal Security Manager

Otro problema que puede llegar a darse y que aun no he resuelto es cuando el navegador trabaja tras un firewall y necesites darle una IP particular al browser como salida a Internet. Aun no se como indicarselo.

Estos casos aun no los resuelvo, así como tampoco he implementado control de los eventos del navegador y del DOM de las páginas deplegadas en él, pero esos puntos me interesa trabajarlos también aunque este ultimo no estoy seguro que tan profundamente se pueda manejar con Gecko# o si será mejor cambiar de enfoque. En cualquier caso practicar el uso de GTK# me cayo bien para refrescar memoria pues hace tiempo no lo usaba más que en GNU/Linux con el diseñador de ventanas STETIC de MonoDevelop.

Acepto sugerencias de mejora y resolución de lo que falta.

Nov 04 2008

Un motivo más para evitar Internet Explorer, 2da parte.

FireFox

Mozilla FireFox

Ya encontre como poder obtener el código fuente de la página cargada en el explorador (Mozilla) montado en nuestra aplicación y como poder sobreescribirlo por otro modificado a nuestro gusto.

// Obtener el contenido de la página cargada en el explorador
mshtml.IHTMLDocument2 pagina = (mshtml.IHTMLDocument2)WebBrowser.Document;
// Obtener su código fuente
string codigoFuente = pagina.body.outerHTML;

///
/// TODO: Modifica el código fuente a tu gusto y necesidad
///

// Sobreescribir el codigo fuente original con nuestro código
doc.body.innerHTML = nuevoCodigoFuente;

La clave esta en usar el objeto de tipo mshtml.IHTMLDocument2. Tuve que volver a agregar la referencia a mshtml.dll porque en las de Mozilla nada más no encontre el objeto IHTMLDocument2, sospecho que no existe equivalente aun o por lo menos no se como se llama. Para agregarla solo puse la referencia como si fuera a hacer un proyecto con el ActiveX de IExplorer. De ahí en fuera, el de Mozilla hace lo demas y entiende los resultados dados por la manipulación de este objeto.

Modifique la aplicación que publique anoche para agregar esta funcionalidad y hacer pruebas. Para usarla hay que enviar como parametros por linea de comando la dirección de la página, el pedazo de código que se desea remplazar y luego el texto que remplazará al parametro anterior.

El código fuente
El ejecutable de prueba

Bueno, y eso es lo que hice con el Mozilla.

Nov 03 2008

Un motivo más para evitar Internet Explorer

Buenas noches mis cinco lectores. Disculparan ustedes que los aburra de nuevo con una entrada de programación. Hagan una flamewar de comentarios fanboys si se aburren.

Resulta que como ya comente antes cree un explorador web personalizado para automatizar unos procesos de unas páginas web que son repetitivos y muy comunes para que me paguen a mi en lugar de a un mono que haga esos rutinarios trabajos. Para hacer esto, inclui en mi aplicación un control ActiveX WebBrowser para manejar el nucleo de Internet Explorer (en este caso el la versión 6) dentro de un formulario, y más importante poder manipular los eventos del explorador (especialmente importante para mi caso es el evento DocumentComplete) y manipular el DOM de las páginas cargadas en mi aplicación para controlar los procesos y manipular su contenido a mi conveniencia. Era lo que estaba a la mano en lugar de construir algo más sofisticado con APIs y a más bajo nivel como uno similar que tenemos en C++ pero es más complejo de entender y dificil de adaptar a casos rebuscados.

El pero esta en que resulta que IExplorer realiza muchas conexiones para abrir la página y el servidor web lo reciente y si la cantidad de trabajo es muy masiva las salidas se saturan. En cambio FireFox para hacer lo mismo solo necesita menos de una cuarta parte del total de conexiones. ¿Qué hace IE con tantas conexiones? ¿Le envia reportes a Redmond de lo que hacemos? No se, pero para 1′000 procesos el IE realiza 30′000 conexiones o más al servidor web mientras que FireFox apenas no anda sobre las 5′200 o menos para las mismas tareas.

¿Es posible usar a FireFox dentro de la aplicación?

Pues si, encontre una manera muy sencilla de hacerlo recurriendo a un ActiveX preparado para cumplir la función del ActiveX de IE ya existente. Lo he probado y en lo general funciona. Advierto de una vez que no es un substituto 1:1 totalmente funcional. Hay diferencias como las hay entre IE y FireFox, ademas de que se nota que le falta trabajo en las cosas más dificiles de implementar. Es decir, para navegación esta bien, pero por lo pronto para manipulación del DOM no he obtenido resultados ya sea por mi desconocimiento de equivalencias o porque estos metodos no estan desarrollados aun o tal vez nunca. Debido a estos inconvenientes es que la investigación continua, pero por lo pronto ya tengo algo interesante. Un WebBrowser personalizado usando el motor de Mozilla, lo cual me parece interesante a mi porque combinando el motor de Mozilla en lugar de IE, usando MONO en lugar de NetFramework y GTK# en lugar de Windows forms podria desarrollar las versiones futuras de modo que sean multiplataforma por lo menos para curiosidad mia. Ademas, entre SharpDevelop que estoy usando ultimamente y MonoDevelop no hay muchas diferencias porque son, como la versión multiplataforma de un IDE común.

FireFox, GTK, MONO

¿Combinación adecuada?

Ahora a lo que te truje. El ActiveX para manejar el motor de Mozilla lo encontre en esta página, desde donde pueden descargar la instalación, que no es otra cosa que la parte indispensable de Gecko (el motor de renderizado) y un registrador de .dll para que puedas usarlas desde los IDE de programación como un control más. Yo estoy usando la versión “Mozilla ActiveX Control 1.7.12” en combinación con FireFox 3. Para integrarlas a Visual estudio solo tuve que agregar una herramienta más en la pestaña de componentes de la caja de herramientas, buscar la .dll entre los componentes COM (previamente he corrido el instalador, claro) y ¡Voila! Ya tengo el control en mi toolbox para agregarlo solo con click y arrastre como cualquier caja de texto. También se pueden integrar a otros IDE para otros lenguajes no .NET y en la página arriba referenciada hay instrucciones para Visual Studio, C++ por código, Delphi y Visual Basic 6.

Si optas por construir desde cero, solo hay que manejar el control y sus eventos como a cualquier otro control de Visual Studio, y es identico en casi todo al de Microsoft incluido en mshtml.dll, hasta se agregan igual a la barra de herramientas, y por eso en aplicaciones existentes basta con borrar los controles de IE, poner en su lugar uno nuevo de FireFox y ponerle el mismo nombre que el anterior, aunque en este caso necesite regenerar los eventos manualmente; y claro. También las referencias a los espacios de nombres AxMOZILLACONTROLLib y MOZILLACONTROLLib

El manejo de este ActiveX es muy similar al de IExplorer, por ejemplo. La declaración del control:

// Declarar el control ActiveXWebBrowser para IExplorer
AxSHDocVw.AxWebBrowser browser = new AxSHDocVw.AxWebBrowser()

// Declarar el control ActiveXWebBrowser para Mozilla
AxMOZILLACONTROLLib.AxMozillaBrowser browser = new AxMOZILLACONTROLLib.AxMozillaBrowser()

Lo unico que cambia es el nombre del objeto y la .dll referenciada. Pero que eso no te engañe, aunque a primera vista y aunque el debuger no te arroje errores, en el evento .Navigate hay un detalle.

// Abrir la página de NGA
browser.Navigate(“http://gamersla.net/”, ref objeto, ref objeto, ref objeto, ref objeto);

En este caso, para IE dos de ellos son tipo object con valor cero y dos son cadenas de caracteres (string) vacias. En cambio, al tratar de usarlo así para Mozilla, solo obtenia ventanas en blanco. Para que el FireFox funcionara tuve que enviar cuatro null de tipo object y entonces si trabajo bien.

ActiveX Mozilla Firefox

Aplicación que utiliza el ActiveX para usar a Mozilla

Construi para hacer pruebas una aplicación sencilla, no es más que una ventana WindowsForm con el control ActiveX para usar a Mozilla donde se abre una página dada como parametro por linea de comando. Les comparto:

El código fuente
El ejecutable de prueba

Ya puedo abrir las páginas, ahora lo que toca es tratar de controlar el DOM y poder hacer cambios y autosubmit en la página controlando los eventos del browser.

Oct 25 2008

Capturando una imagen desde una página web

Sharp Develop

Sharp Develop

Buenas noches mis queridos cinco lectores. Como ya alguna vez he comentado antes, una parte de mi trabajo que me gusta es la investigación y desarrollo de soluciones nuevas. Soy programador, así que eso implica el desarrollo de sistemas o programas que hagan cosas que ninguno de los que ya tenemos desarrollados hacen.

Esto me gusta porque es muy interesante e incluso hasta si el objetivo no es alcanzado (cosa que no recuerdo que halla pasado, solo se tarda más) siempre aprendo algo, y por otro lado, al ser una cosa novedosa, no hay una medida fiable de los tiempos por lo que este tipo de desarrollos no me imponen un cronograma de trabajo y entregas que cumplir a rajatabla. Estará cuando este. También tiene la ventaja de que “estará cuando este” no le gusta a los jefes, así que me dan recursos y facilidades para acelerar el trabajo: internet libre y que yo no atienda llamadas si no es indispensable.

Bueno, basta de charla introductoria y vamos a lo que te truje… ¿Y de qué nos vas a hablar ahora Gnoblis? Pues de la investigación de turno que me ha absorbido los últimos días y que da titulo a esta entrada.

LA SITUACIÓN

La situación es esta; necesito hacer un repetitivo tramite en una página que implica el llenado de un formulario en una página web que no esta bajo mi control por pertenecer a un tercero, entonces le hago llegar la información a un web browser personalizado que hice basándome en el control ActiveX WebBrowser para controlar el motor de Internet Explorer (esto correrá sobre un Windows XP, es seguro que tenga IE), y así controlo automáticamente todo el proceso. La novedad es que introdujeron en la página una de esas imágenes de códigos generadas al azar con texto muy ofuscado para que no lo entienda ningún proceso automático de reconocimiento de caracteres.

El plan es poder obtener la imagen que el usuario debe reconocer, enviársela y obtener lo que el usuario tecleo. Para obtener la imagen probé varias cosas. La primera fue apoyarme con una aplicación que desarrolle para tomar una captura de pantalla automáticamente y con la capacidad de tomar todo el escritorio o recortar un recuadro indicado como parámetros. Lo primero fue solo incluir una llamada a esta aplicación a la que bautice ScreenShoter y de la que tal vez les cuente en otra ocasión. Pues bien, funciono pero solo es un uso temporal mientras se desarrolla la solución definitiva porque tiene dos graves inconvenientes.

- El explorador debe estar al frente al momento de la captura de la imagen.
- No funciona si se cierra la sesión, es decir, requiere un monitor al servidor siempre.

Debido a esto se requiere un diseño en la aplicación que permita la convivencia de múltiples instancias del programa de manera que tomen turnos para tomar el foco y ponerse al frente, y no se puede trabajar en esa PC porque si atraviesas algo frente a los exploradores la imagen que deseamos no saldrá. Por eso, esto fue un parche temporal para mantener la producción mientras al investigación real seguía.

Tenemos algunas limitantes, como el tiempo de desarrollo. Necesitamos una solución razonablemente rápida. También el uso de C# con el Framework 1.1 debido a que la plataforma de desarrollo de la empresa es Visual Studio 2003 y porque la aplicación base (el explorador personalizado) esta desarrollada con esto, y esta funcionalidad solo es un agregado al programa ya existente.

No se ha avanzado a un framework mayor porque las aplicaciones se distribuyen a muchísimas terminales y enviarlas requeriría la instalación de ese framework a todas las terminales, cosa que cansa solo de pensarlo. Se hará algún día pero hoy no.

INTENTOS QUE FRACASARON MISERABLEMENTE

Un compañero intento realizar una aplicación para captura de imagen de ventanas utilizando C++ y el control de PIXEL para tomar directamente la imagen desde la memoria de vídeo, a un nivel más bajo que el actual ScreenShoter. Esta aplicación recibiría por parámetro el Handler de la ventana a la que queríamos tomar la imagen y así se controlaba el uso de múltiples instancias o que la ventana escogida no tuviera el foco. Y funciono hasta que se cerraba la sesión, a partir de ahí solo obtenemos recuadros negros. Al parecer si no hay sesión abierta, esta memoria no se utiliza, así que este método seguía necesitando un monitor y una sesión abierta.

Al tiempo que pasaba eso yo empecé a desarrollar un método para obtener la imagen directamente del axWebBrowser de la misma aplicación que controla la imagen.

Usando el objeto IHTMLElementRender y su metodo DrawToDC se puede pasar a una imagen de mapa de bits la figura de un elemento de la página abierta en el ActiveX WebBrowser o en una instancia activa de Internet Explorer (mediante handlers). Aunque en realidad solo logre hacerlo exitosamente en C++, donde puedes montar el mismo control AxWebBrowser usando MFC o API para crear ventanas pero esa es otra historia y no era el caso ponerse a reescribir la aplicación entera a C++ solo porque yo no encontré el modo de hacerlo funcionar en C#, que se debe de poder si investigo más a fondo pero no hay tiempo.

// Obtener el contenido de la página cargada en el explorador
IHTMLDocument2 doc = (IHTMLDocument2)axWebBrowser.Document;
IHTMLElement body = (IHTMLElement)doc.body;
IHTMLElementRender render = (IHTMLElementRender)body;

El código anterior copia el cuerpo de la página a un objeto IHTMLElementRender, que usaremos para dibujar el objeto en una imagen de mapa de bits que será igual a lo que se ve en el IExplorer. No es necesario usar el render para toda la página, pueden manejarse objetos individuales como en mi caso que solo quiero una imagen en especial. Por ejemplo, puedes localizar un elemento por su posición en la página si conoces sus coordenadas invocando a IHTMLDocument2.elementFromPoint(int x, int y); donde X y Y son las coordenadas. También esta IHTMLDocument2.images para obtener la colección de imágenes en la página y ya de ahí tomar la necesaria. Yo use esto ultimo porque así no me afecta la resolución de la maquina donde ponga la aplicación. Hay otras formas de obtener elementos específicos y ya no profundizare en ese aspecto.

El metodo DrawToDC para dibujar el control necesita un apuntador a una imagen de mapa de bits donde guardar la imagen que dibujará, así que además del elemento cargado en el render, también necesitamos un apuntador, una imagen de mapa de bits y un objeto Graphics.

// Imagen de mapa de bits que contendra el elemento dibujado
Bitmap imagen = new Bitmap(ancho, alto);
Graphics grafico = Graphics.FromImage(imagen); // Ligar el Bitmap con el objeto Graphics
IntPtr hdc = grafico.GetHdc(); // Crear el apuntador al objeto grafico

Hasta aquí todo va muy bien, pero todos sabemos que cuando algo va tan bien es porque hay algo muy malo que no viste, y en este caso el punto esta en que el parámetro que recibe el metodo DrawToDC del render no es un puntero cualquiera, no, que va. Es un objeto del tipo mshtml._RemotableHandle que nunca supe bien como manejar. Si envías un puntero normal a DrawToDC, este simplemente marca error por parametro de tipo incorrecto. Intente crear una instancia de mshtml._RemotableHandle y pasar el valor del puntero común a este, pero a pesar de que ya no marcaba error, en realidad nunca logre resultados exitosos.

// Inicializar un objeto _RemotableHandle
mshtml._RemotableHandle manejador = new _RemotableHandle;
manejador.fContext = 0×48746457;
// Esto depende de tu sistema operativo y procesador si es de 32 o 64 bits
manejador.u.hInproc = grafico.GetHdc().ToInt32();
render.DrawToDC(ref manejador);

En C++ no tienes estos problemas porque puedes generar el puntero con la instrucción CreateCompatibleDC que funcionan para DrawToDC, así que me plantee agregar la DLL gdi32.dll como referencia a mi proyecto para utilizar el método como en C++ pero otra cosa que probé antes me detuvo. Procedí a sobrecargar el método DrawToDC para que aceptara punteros comunes. Agregue el siguiente código al principio de mi clase

[Guid("3050f669-98b5-11cf-bb82-00aa00bdce0b"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown),
ComVisible(true),ComImport]
interface IHTMLElementRender
{
void DrawToDC([In] IntPtr hDC);
void SetDocumentPrinter([In, MarshalAs(UnmanagedType.BStr)] string bstrPrinterName, [In] IntPtr hDC);
};

Ya con esto podemos enviar variables IntPtr al metodo DrawToDC del objeto render como algo normal.

// Guardar la página en la imagen bitmap
IHTMLDocument2 doc = (IHTMLDocument2)axWebBrowser.Document;
IHTMLElement body = (IHTMLElement)doc.body;
IHTMLElementRender render = (IHTMLElementRender)body;
Bitmap imagen = new Bitmap(ancho, alto);
Graphics grafico = Graphics.FromImage(imagen);
IntPtr hdc = grafico.GetHdc();
// Liberar memoria que ya no necesitamos
grafico.ReleaseHdc(hdc);
grafico.Dispose();
// Guardar la imagen en el disco duro como .jpg
imagen.Save(“c:\\captura_de_pantalla.jpg”, ImageFormat.Jpeg);

En la bella teoría funciona, pero ya en la practica, al igual que la primera aplicación, al cerrar la sesión solo salva recuadros negros. Hora de aplicar el plan C… solo que en ese momento me habría gustado tener más claro cual era ese plan.

EXITO

Después de investigar un rato dí con el método DrawToBitmap. Esta instrucción es capaz de dibujar un control de una ventana como una imagen de mapa de bits, solo que no existe en el NetFramework 1.1, pues apareció con la versión 2.0

En fin, me la jugue. Para poder hacer el desarrollo instale SHARP DEVELOP 2.2, un IDE de programación .NET opensource gratuito. Hice una copia del proyecto, la cargue con Sharp Develop y lo actualice al nuevo framework y me puse a implementar el cambio.

// Declarar una imagen del tamaño del explorador
Rectangle area = new Rectangle(0, 0, axWebBrowser.Width, axWebBrowser.Height);
Bitmap imagen = new Bitmap(axWebBrowser.Width, axWebBrowser.Height);
// Obtener la imagen de la página cargada en el explorador
axWebBrowser.DrawToBitmap(imagen, area);

¡Y voila!, con eso tenemos la imagen de la parte visible de la página. Según investigue, para obtener toda la página y no solo la parte visible esta la posibilidad de usar un método sobrecargado para inicializar el objeto Rectangle para tomar toda la página.

Rectangle area = new Rectangle(0, 0, axWebBrowser.Document.ActiveElement.ScrollRectangle);

Lo intente pero siempre me dio una excepción “‘object’ does not contain a definition for ‘ActiveElement’ (CS0117)” y como yo no necesito toda la página pues no me puse a buscar a fondo porque ni como usarlo, pero ahí queda para que quien necesite una imagen de toda la página completa pueda ver si le sirve como punto de partida.

Bueno, como ya había mencionado, yo no quería toda la página, solo una parte. Para obtener la parte que quiero hice lo siguiente.

// Imagen que guardara el recuadro que me interesa
Bitmap recorte = new Bitmap(ancho, alto, PixelFormat.Format24bppRgb);
// Área de la imagen que quiero obtener
area = new Rectangle(x, y, ancho, alto);
// Tomar este recuadro de la imagen original completa
recorte = imagen.Clone(area, PixelFormat.Format24bppRgb);
// Guardar la imagen en el disco duro como .jpg
recorte.Save(“c:\\imagen.jpg”, ImageFormat.Jpeg);

La función Bitmap.Clone() me permite obtener una copia de una imagen, ya sea completa o solo de las coordenadas y tamaño de un rectángulo indicado. También use en esta aplicación el parámetro PixelFormat.Format24bppRgb porque la aplicación que hará uso de estas imagenes solo soporta esa compresión como máxima calidad. El poder dar formato desde aquí nos ahorro un paso porque el ScreenShoter toma las imágenes a 32 bpp y usábamos una instrucción de ImageMagick para convertirlas antes de que la aplicación final empezará a trabajar con las imágenes.

Y bueno, mis cinco lectores. Perdón por aburrirlos pero esta semana eso hice en el trabajo y queria publicar lo que encontré por si a alguien más le sirve, pues yo también busque en páginas durante la semana, todo la investigación y desarrollo termina expresado como unas lineas de código.

Sep 30 2008

Lectura obligada para todo informatico

Memorias de un ingeniero

Buenas tardes mis queridos cinco lectores. Bienvenidos nuevamente al rincón literario de la madriguera. Esta vez abordaremos el mundo literario con una relectura que rompe con la continuidad de las cronicas vampiricas y nos regresa al mundo real, demasiado real por desgracia.

El libro número trece del año es Fuckowski, memorias de un ingeniero. Escrito por Alfredo de Hoces García-Galán en el año 2005.

Esta obra narra experiencias y filosofia de Fuckowski; un informatico cualquiera. Programador de una empresa de desarrollo de software, salido de una familia cualquiera y estudiante de escuela publica como el común de los mortales.

Es un libro con el que en varios momentos uno facilmente puede ponerse en los zapatos del protagonista y descubrir a los Pijoskis de su propio entorno, las situaciones cotidianas de las empresas donde administrativos profanos meten la nariz en las cosas tecnicas y toda la fauna de oficina que es común y hasta estereotipada.

Incluso si no estan relacionados con la informatica pueden divertirse leyendo este libro porque el autor evito los ejemplos tecnicos o los chistes demasiado geek a la “Raulito el friki” por analogias muy acertadas, como la cartera de huevos, el porsche/bicicleta o pichon que ejemplifica el cambio entre programador y analista externo.

En mi oficina yo tengo como archivos compartidos y de lectura obligatoria tres documentos.

* Un FAQs de situaciones comunes que no quiero estar explicando una y otra vez y no pienso aprenderme de memoria.
* Un glosario de frases celebres y de uso común en mi centro.
* Fuckowski, memorias de un ingeniero.

De hecho, la clasificación de proyectos bicicleta ya se ha vuelto de conocimiento común en la oficina. Han salido varios ejemplos de cosas de las que el libro ya habla y resulta divertida la analogía.

Es una especie de humor sobre lo cotidiano, tomarnos con humor lo malo y empezarlo a mejorar una vez identificado.

Descarga Directa. Leelo ¡No es opcional!

Jul 25 2008

Semana ajetreada

Buenas noches mis cuatro lectores. Esta semana ha sido laboralmente exigente debido a que el proximo domingo 27 entra en vigor una normatividad del gobierno para las AFORES (Empresas que administran los fondos de ahorro para el retiro de los trabajadores en México) para evitar robos de clientes y afiliaciones ilegales principalmente… y de paso meter más burocracia al tramite.

El caso es que como a Gnoblis no lo gusta ser molestado el fin de semana estos días le estubo poniendo mucho empeño a su trabajo y los frutos son dulces. Hoy se llego la hora de la salida, toda la oficina haciendo pruebas, los administrativos de la Ciudad de México en la linea telefonica y en conferencias constantes. Ajustes de ultima hora en las aplicaciones por aquí y por halla, acuerdos y afirmaciones para que todas las piezas del proceso embonaran y yo anunciando que me hiba a casa… así tan campante XD

Han de saber que yo soy programador, así que mi trabajo esta en desarrollo y mantenimiento de sistemas o por lo menos de partes de ellos para automatizar lo más posible los tramites y minimizar las posibilidades de error por medio de atajar lo más pronto posibles los motivos de rechazo en los tramites de afiliación.

El gerente me para el alto y me dice que no me valla aun hasta probar todo, pregunto yo si va a pagar horas extras, me dice un tibio tal vez así que contesto con un “tal vez me quede entonces” al tiempo que sigo avanzando hacia la puerta… me vuelve a llamar y me pregunta por mis aplicaciones, una especie de checklist.

Basicamente mi tarea es mantener el sistema de captura de nuevos clientes que se encuentra en los modulos de las tiendas, el menú que maneja toda aplicación que los empleados de tienda requieren y bloquear el uso de todo lo demas. Mi especialidad es lo que tenga que ver con sistemas web. Esta es la parte más o menos rutinaria pues es mayormente mantenimiento y un poco de atención al usuario y otro poco de desarrollo nuevo de vez en cuando.

Ese desarrollo nuevo y un poco en investigacíón de cosas novedosas para nosotros en nuestra área es la parte divertida del trabajo, como por ejemplo la aplicación esa para convertir las páginas de archivos PDF a imagenes individuales de la que hable antes y surgio cuando necesitamos manipular esos documentos. Me gusta porque me dan un tiempo de hacer basicamente lo que se me ocurra mientras funcione de manera razonablemente buena, sea factible y practico. Siempre aprendo.

Bueno, me desvie del tema. Decia que me esforce y todas mis aplicaciones fueron probadas ayer u hoy y ya 40 minutos antes de la hora de salida ya estaba todo identificado, documentado, visto por gente administrativa, copias repartidas a quien hiciera falta, respaldado en el repositorio y ademas deje a dos chavales que estuvieron involucrados en el desarrollo y pueden atender detalles de emergencia y que salen hasta las 22:00.

Fue divertido ver al gerente buscando excusas para que me quedará… no encontro ninguna y le pregunto a los otros desarrolladores si alguno necesitaba algun apoyo por que usase alguna de mis aplicaciones, pues todos los demas se quedaron ahí porque les faltaba alguna cosa u otra… pero nada, ningún pendiente relacionado conmigo. Y aquí estoy, bastante agusto.

Sali y me fui caminando a casa bastante feliz; la tarde, caminata y powA metal en mis audifonos. Llego y gracias a Dios, la casa vacia…. oh, tranquilidad. Hice la prueba fisica del día en el Wii Fit y sale 82.4 Kg, que historicamente es el menor peso que he tenido en 43 días que llevo con el Wii Fit.

Después de eso me puse a jugar Smash Bros Brawl, luego a cenar y mirame ahora, en mi habitación escribiendoles esto al tiempo que escucho unas canciones del grupo Saratoga que Kid hizo el favor de hacerme saber que existia, son un grupo español de metal, algunas canciones buenas.

Mañana sabado… quiero ver Batman. Por ahora le doy gracias a Jehová porque me esta tratando muy bien hoy. Bueno, me retiro y buenas noches a los cuatro. Saludos.

Jul 16 2008

PDF2Imagen, la venganza

Debido al exito del PDF2Imagen, aplicación para convertir archivos PDF en imagenes. Un lector me pidio el código fuente así que le di una acomodada para que se vea presentable y lo publico hoy. Si, lo se, esta es una entrada “de nicho” para un tipo de lector muy especifico pero no se preocupen que hay Gnoblis para casi todos por lo regular.

Ejecutable

Código fuente

Para quienes no hallan visto o no recuerden la entrada original donde publique información sobre mi programa para convertir en imagenes las páginas de un archivo PDF y las motivaciones que me llevaron a hacerlo, que basicamente era automatizar una tarea tediosa y de gran volumen que implicaba obtener un monton de PDF casi iguales, convertir las páginas a imagenes, recortarles unos pedazos a las paginas que interesaban y formar una imagen nueva con esos pedazos en diferentes grados de calidad y contraste para obtener el resultado requerido… una y otra vez hasta la locura y el final de los tiempos, así que mejor hice una aplicación automatica especifica para eso y que corre en background… y mientras investigaba realice esta que es más general y me permitio experimentar para generarla.

PDF2Imagen

Así se ve la aplicación actualmente.

La aplicación esta programada en C#, utiliza al objeto COM de ImageMagick para realizar las conversiones, que a su vez se apoya en GhostScript para tener compatibilidad con los PDF. Visualmente es muy sencillo como veran pues en realidad uso muy pocos parametros, y por dentro el código tampoco es completo pues practicamente solo es una interfaz grafica para ImageMagick.

Adicionalmente publico al ejecutable y el código fuente que les doy también quiero mencionar un par de funciones que me fueron utiles para mi proposito especifico pero en esta aplicación de conversión general no las he implementado, pero igual les doy las sintaxis de ello.

Para recortar segmentos de imagen hay que usar en la función convert (crea imagenes nuevas) o mogrify(sobreescribe la imagen original) de ImageMagick el parametro -crop seguido de una idicación denominada geometria, la cual consiste en indicar el tamaño del área a recortar y el punto donde comienza el recorte. Por ejemplo 800×600+10+20 significa recorta un recuadro de 800 pixeles de ancho por 600 pixeles de alto a partir del pixel 10 de izquierda a derecha y el pixel 20 de arriba a abajo de la imagen original. La instrucción se veria algo así:

ImageMagickObject.MagickImageClass imagen = new MagickImageClass();
object[] parametros = new object[4];

parametros[0] = “-crop”;
parametros[1] = “800×600+10+20″;
parametros[2] = “\imagen_original_a_la_que_le_voy_a_cortar.jpg”;
parametros[3] = “\imagen_nueva_que_es_solo_la_parte_que_recorte.bmp”;

imagen.Convert(ref parametros);

Aunque también se puede usar combinada con la instrucción mogrify en lugar de convert para sobreescribir la imagen original en lugar de crear una nueva con el reporte, de la siguiente manera:

ImageMagickObject.MagickImageClass imagen = new MagickImageClass();
object[] parametros = new object[3];

parametros[0] = “-crop”;
parametros[1] = “800×600+10+20″;
parametros[2] = “\imagen_a_la_que_le_voy_a_cortar_y_sobreescribir.tif”;

imagen.Mogrify(ref parametros);

En caso contrario a crop, para colocar una imagen sobre otra se usa Composite, esta instrucción coloca una imagen sobre otra y guarda la imagen que se genera. También hace uso de una variante del parametro de geometria para indicar la posición donde se colocará la imagen de frente.

ImageMagickObject.MagickImageClass imagen = new MagickImageClass();
object[] parametros = new object[5];

parametros[0] = “c:\imagen_de_frente.jpg”;
parametros[1] = “c:\imagen_de_fondo.gif”;
parametros[2] = “-geometry”;
parametros[3] = “+10+20″;
parametros[4] = “c:\nombreNuevaImagen.png”;

imagen.Composite(ref parametros);

Notese que en este caso el valor de geometria solo debe indicar la posición donde se colocará la imagen de frente sobre la imagen de fondo. Aprender el uso de las indicaciones de geometria es muy útil para utilizar con exactitud instrucciones más avanzadas y de efectos más complejos que yo aun no utilizo.

Bueno, aquí queda para compartirlo con quien le sirva y que quien lo quiera mejorar pueda hacerlo.

Jul 02 2008

Convertir en Imagenes las páginas de un archivo PDF

Hace tiempo que no tocaba un tema de programación en este blog, pero surgio la necesidad de imprimir un documento que viene dentro de un archivo PDF que tenemos que obtener de otro lugar para en teoría llenarlo a mano una vez impreso pero debido al volumen de movimientos respectivos no lo queremos hacer manualmente. Otra cosa a considerar es que la aplicación debe correr en Windows XP, lo remarco porque para GNU/Linux hay algunas aplicaciones pequeñas que hacen el trabajo de manera eficiente, algunos editores de imagenes y otras en linea de comandos. En Windows las que encontre me las vendian y eran pantallas, cosa que no me sirve, en realidad yo necesitaba algo automatico que fuera manejado dentro de una aplicación más grande que necesita obtener como imagen el contenido del PDF para poder manipular la imagen… en fin por eso mejor desarrolle mi propio metodo.

Entonces manos a la obra. Mi parte aqui fue primeramente obtener la página del PDF como una imagen sin que esta sufriera deformaciones. Para lograr mi objetivo decidi usar el objeto COM del Acrobat Reader dentro de una aplicación .NET en el lenguaje C# porque es lo que manejo menos peor y estoy en Windows por motivos de trabajo aunque no pasa mucho tiempo para darme cuenta de lo poco viable de este modo. ¿Alternativas?

El segundo intento pasa por una googleada para documentarme al respecto en lugar de reinventar la rueda. Casi todo lo que encuentro son páginas tratando de venderme aplicaciones que hacen converciones de PDF a otras cosas desde ventanas… no me interesa. Por fin después de la busqueda llego a la conclución de que se puede usar ImageMagik + GhostScript para lograr mi objetivo.

ImageMagik es un potente editor de imagenes capaz de ser usado desde linea de comandos con versiones disponible para GNU/Linux, Mac, Windows y Unix… supongo que también ha de estar portado a BSD. Ademas de eso tiene clases y objetos preparados para su uso en diversos lenguajes de programación. La licencia es GPL y el código fuente esta disponible.

Por su parte ghostscript es un interprete de archivos postscript y PDF. En este caso es necesario para brindar a ImageMagick de compatibilidad con PDF. Esta aplicación permite visualizar e interpretar los archivos. GNU Ghostscript viene ya instalado en muchas distribuciones de Linux, si usas Windows vas a ocupar instalarlo tu mismo.

Consegui la DLL Interop.ImageMagickObject.dll, la agrego a las referencias de la aplicación para poder usar sus metodos en la conversión por medio de una instancia de la clase MagickImageClass. En este caso el metodo que me importa es el metodo CONVERT. Para esto use una función así:

public static bool convertirPDFenImagenes(string archivoOriginal)
{
ImageMagickObject.MagickImageClass imagen = new MagickImageClass();
object[] parametros = new object[8];
string archivoNuevo = string.Empty;

// %04d indica que las imagenes se van a llamar igual que el archivo
// original más un número secuencial 0001, 0002, 0003 y así
// sucesivamente. El 4 es la cantidad de digitos 0 que agregara.

archivoNuevo = archivoOriginal + “%04d.jpg;

// Parametros de conversión. Esta es la calidad que me sirve.
parametros[0] = “-density”;
parametros[1] = “300″;
parametros[2] = “-quality”;
parametros[3] = “100″;
parametros[4] = “-define”;
parametros[5] = “pdf:use-cropbox=true”;
parametros[6] = archivoOriginal;
parametros[7] = archivoNuevo;

try // Realizar la conversión.
{

imagen.Convert(ref parametros);
return true;
}
catch (Exception ex)
{
return false;
}
}

Al estar hecha en C# puede aplicarse por igual en Windows usando el NetFramework o Mono en Visual Studio o en Sharp Develop, o en Linux usando Mono + Mono Develop o cualquier editor de texto si prefieren, y tampoco es dificil traducirla a otros lenguajes de .NET o aun distintos si tienes la capacidad. Lo que más varia seria como referenciar al ImageMagick.

Esta función convierte todas las páginas del PDF en imagenes .jpg, pero puedes adaptarlo para otros tipos de imagen con solo cambiar la extención. Lo he probado para tif, gif y png también. Si no necesitas convertir todo el archivo PDF puedes agregar al archivo original unas [] con un rango dentro, por ejemplo archivo.pdf[4] o archivo.pdf[1-6] tomando en cuenta que la primera página es la cero, no la uno.

¿Y esto funciona? ¿Tiene una aplicación practica para mis lectores? Bueno, pues tal vez sí. Si gustan probar, en base al código fuente arriba mostrado hice otra aplicación menor para mi mismo que hace la conversión de manera sencilla y rapida desde una ventana simple en Windows. Cuando quieras convertir un PDF en imagenes sin pagar un peso ni tener marcas de agua sobre ellas y amenazas de cobros de licencia y esas cosas. Pueden descargarla desde nuestros servidores:

PDF2JPG.exe. Ejecutable, como es de .NET necesitas tener el NetFramework 1.1 o superior o MONO equivalente, aunque las Windows Forms podrian dar problemas.

Interop.ImageMagickObject.dll. Esta DLL es necesaria pues es el objeto que tiene las opcionesd de ImageMagick. Colocala junto al ejecutable.

También vas a necesitar tener ImageMagick y GhostScript. Puedes descargar los instaladores desde sus páginas oficiales arriba enlazadas. Por cierto, por cuestiones practicas laborales no puedo andar corriendo instaladores completos, así que para poner ImageMagick también esta esta opción. Descarga este paquete, el cual contiene una carpeta llamada ImageMagick, la cual debes ponerla en “Archivos de Programa” y luego ejecuta el ImageMagick_OK.reg para que agregue el ImageMagick al registro de Windows y lo puedas usar. Considera eso si quieres evitar la fatiga de ir al sitio de ImageMagick e instalarlo completo.

Feb 20 2008

Conversión de BBcode a HTML – Hagalo usted mismo

Cuando comenzo el proyecto de la guía de moveset, esta empezo como una lista en un tema de foro, por lo tanto el formato fue hecho en BBcode para foro. Cuando se dio la oportunidad de publicarla en páginas me vi en la necesidad de convertir la guía a código HTML, cosa por demas tediosa dado el volumen que ya en ese entonces tenia la guía.

Para convertir todo use una aplicación donde pegaba el código BBcode y me regresaba su equivalente en HTML. Todo muy bien hasta ahí. En enero fue la ultima vez que lo utilice para actualizar la guía.

Llego mediados de febrero y el día 16 tuve lista la nueva actualización de la guía y me disponia a actualizarla pero existieron algunos inconvenientes. La mayoria de los moveset que encuentro son posteados en foros, así que siguen viniendo en BBcode, que solo inserto al archivo original, el cual convierto y publico, solo que desde hace un mes que el windows se descompuso y la aplicación que usaba no corre bien en el Wine, no me deja meter los archivos completos y se cierra de manera inesperada.

¿Qué haria de hacer Gnoblis? ¿Arreglaria el windows? ¿Buscara una aplicación alternativa? Ni una ni otra. Gnoblis tiene una tendencia al hagalo usted mismo que sirvio de inspiración para resolver este sencillo problema. Mis cuatro lectores recordaran que Gnoblis gusta de programar, y eso hizo. Desarrolle una pequeña aplicación de consola que realiza la conversión. Simplemente lo pongo en la carpeta donde tengo los archivos originales en BBcode, lo ejecuto y me deja unos nuevos en HTML que ya solo abro, copio y pego en la página.

Hice una leve adaptación para poder usar la aplicación en consola con parametros de linea de comando para que no se limite a algo fijo como la primera que hice. Funciona tanto en windows como en linux si tienes Mono o NetFramework en la PC y dejo la aplicación y el código importante para quien curiosear o mejorarla porque esta asquerosamente simple y me ha dado una flojera crear una interfaz grafica que es posible encimar sin mucho esfuerzo y tal vez haga después. He aquí los enlaces

Aplicación y Código Fuente

Qué lo disfruten ingratos.