Artículo original: Captcha Image Generator with GdiPlus-X
https://vfpimaging.blogspot.com/2012/03/captcha-image-generator-with-gdiplusx.html
Autor: Cesar Ch.
Traducido por: Ana María Bisbé York
Recientemente, estaba navegando por el Blog de Rick Stralh cuando encontré un escrito muy interesante, "A Captcha Image generator for FoxPro", en el cual el muestra cómo crear algunas imágenes "captcha".
Nota de la Traductora: CAPTCHA es el acrónimo de "Completely Automated Public Turing test to tell Computers and Humans Apart", y consiste en una verificación automatizada para determinar si el usuario que realiza la petición es humano o no.
Como ha dicho Rick Stralh en su escrito, "CAPTCHA muestra básicamente una imagen de verificación junto a un cuadro de texto que tiene que ser llenado para verificar la petición actual. No es un procedimiento infalible para la validación y tiene algunos problemas de accesibilidad; pero parece que es la solución más frecuente a este problema." La imagen generada contiene un texto aleatorio.
En este caso, Rick Stralh escogió crear una DLL en C++, y la llama desde VFP. Aquí mostraré cómo obtener los mismos resultados utilizando la nueva biblioteca GdiPlus-X.
El código que se muestra a continuación pudiera ser útil para otros propósitos también, porque se utilizaron algunas técnicas de dibujo muy interesantes.
Para obtener la cadena aleatoria, creé una función muy simple, que recibe como un parámetro de cantidad de caracteres que se crearán.
PROCEDURE CreateString(tnLength
LOCAL lcText, lcChar, x, n
lcText = ""
FOR n = 1 TO tnLength
x = INT(RAND() * 36)
lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
lcText = lcText + lcChar
ENDFOR
RETURN lcText
ENDPROC
¡ A JUGAR !
IMPORTANTE:
Todos los ejemplos que se muestran emplean la nueva biblioteca GDIPlus-X, que está aun en versión ALPHA; pero es estable y fiable para hacer la mayoría de las tareas de GDI+. Descargue la última versión estable de Codeplex:
http://www.codeplex.com/Wiki/View.aspx?ProjectName=VFPX&title=GDIPlusX
EJEMPLO 1 - CREAR UN IMAGEN SENCILLA Y DIBUJE ALGÚN TEXTO
** Generador de imágenes Captcha
** Crea una imagen muy sencilla que contiene una cadena
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha
LOCAL lcText AS Character
lcText = CreateString(8)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
LOCFILE("system.vcx","vcx")))
LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics
WITH _SCREEN.System.Drawing
loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && Font Name, Size
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height)
* Obtiene un objeto Graphics para dibujar
loGfx = .Graphics.FromImage(loBmp)
loGfx.Clear(.Color.White)
* Dibuja toda la cadena
loGfx.DrawString(lcText, loFont, .Brushes.Black, 0, 0)
* Guarda la imagen en disco
loBmp.Save("c:\captcha1.png", .Imaging.ImageFormat.Png)
ENDWITH
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha1.png
RETURN
PROCEDURE CreateString(tnLength)
LOCAL lcText, lcChar, x, n
lcText = ""
FOR n = 1 TO tnLength
x = INT(RAND() * 36)
lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
lcText = lcText + lcChar
ENDFOR
RETURN lcText
ENDPROC
El ejemplo de arriba crea la imagen más sencilla, que contiene una cadena aleatoria dentro. para obtener el tamaño necesario para crear el Bitmap, en el primer momento, he creado un objeto Graphics desde _Screen. Como se puede ver a continuación, este objeto tiene solamente una tarea: permitirnos llamar a la función MeasureString para obtener el tamaño necesario para nuestro bitmap.
loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && FontName, FontSize
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height)
EJEMPLO 2 - CREA UNA IMAGEN SENCILLA Y DIBUJA UN TEXTO SOBRE UN FONDO TRAMADO.
** Generador de imágenes Captcha
** Crea una imagen muy sencilla que contiene una cadena con un fondo tramado
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha
LOCAL lcText AS Character
lcText = CreateString(8)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
LOCFILE("system.vcx","vcx")))
LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics
WITH _SCREEN.System.Drawing
loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && Font Name, Size
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(loSizeF.Width, loSizeF.Height)
* Obtiene un objeto Graphics para dibujar
loGfx = .Graphics.FromImage(loBmp)
loGfx.Clear(.Color.White)
liRand = INT(RAND()*52) + 1
* Rellena el fondo del rectángulo con una brocha tramada de forma aleatoria
loGfx.FillRectangle( ;
.Drawing2D.HatchBrush.New(liRand, .Color.Black, .Color.White),;
0,0,loBmp.Width,loBmp.Height)
* Dibuja toda la cadena
loGfx.DrawString(lcText, loFont, .Brushes.Black, 0, 0)
* Guarda la imagen en disco
loBmp.Save("c:\captcha2.png", .Imaging.ImageFormat.Png)
ENDWITH
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha2.png
RETURN
PROCEDURE CreateString(tnLength)
LOCAL lcText, lcChar, x, n
lcText = ""
FOR n = 1 TO tnLength
x = INT(RAND() * 36)
lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
lcText = lcText + lcChar
ENDFOR
RETURN lcText
ENDPROC
Este es exactamente el mismo ejemplo que utilizamos antes; pero agregando un fondo tramado. GDI+ ofrece 52 plantillas diferentes para brochas con tramas. En este ejemplo, preferí utilizar uno aleatorio. Verifíquelo y seleccione el que le guste más:
* Rellena el fondo del rectángulo con una brocha tramada de forma aleatoria
loGfx.FillRectangle( ;
.Drawing2D.HatchBrush.New(liRand, .Color.Black, .Color.White),;
0,0,loBmp.Width,loBmp.Height)
EJEMPLO 3 - DIBUJAR ALGÚN TEXTO CON UN FONDO COLOREADO UTILIZANDO COLORES ALEATORIOS
** Generador de imágenes Captcha
** Crea una imagen muy sencilla que contiene una cadena
** Cada carácter en color aleatorio diferente con un color de fondo aleatorio
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha
LOCAL lcText AS Character
lcText = CreateString(8)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
LOCFILE("system.vcx","vcx")))
LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics
WITH _SCREEN.System.Drawing
loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && Font Name, Size
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
LOCAL lnWidth, lnHeight, lnCharWidth
lnWidth = loSizeF.Width * 1.5 && Se estrecha al 50% para garantizar que quepan los caracteres rotados
lnHeight = loSizeF.Height
lnCharWidth = lnWidth / LEN(lcText)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(lnWidth, lnHeight)
* Obtiene un objeto Graphics para dibujar
loGfx = .Graphics.FromImage(loBmp)
loGfx.Clear(.Color.White)
* Llena el fondo del rectángulo con una brocha sólida de color aleatorio
* Necesitamos aquí un color pastel color, por lo que haremos que cada canal sea al menos 180
* Dibuja el rectángulo de fondo
loGfx.FillRectangle( ;
.SolidBrush.New(.Color.FromARGB(255, INT(RAND() * 76)+180, ;
INT(RAND() * 76)+180, INT(RAND() * 76)+180)), ;
0,0,loBmp.Width,loBmp.Height)
* Dibuja cada carácter en un color aleatorio diferente
LOCAL n, lcChar
LOCAL loBrush AS xfcSolidBrush
FOR n = 0 TO LEN(lcText)
lcChar = SUBSTR(lcText, n, 1)
* Crea una brocha con un color aleatorio
loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF)))
* Dibuja el carácter
loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)
ENDFOR
* Guarda la imagen en disco
loBmp.Save("c:\captcha3.png", .Imaging.ImageFormat.Png)
ENDWITH
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha3.png
RETURN
PROCEDURE CreateString(tnLength)
LOCAL lcText, lcChar, x, n
lcText = ""
FOR n = 1 TO tnLength
x = INT(RAND() * 36)
lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
lcText = lcText + lcChar
ENDFOR
RETURN lcText
ENDPROC
Esta vez decidí crear un fondo utilizando una brocha sólida con un valor aleatorio en BackColor. Quise asegurarme de obtener un color pastel, por que utilicé:
INT(RAND() * 76) + 180 para cada canal de color RGB. Esta forma, cada canal tendrá al menos el valor 180, asegurando que obtenga un color brillante.
También muy sencillo dibujar cada color en un color aleatorio. Solamente hice un lazo de la cadena y dibujé cada caracter en su posición proporcional. Este momento, deseo tener cualquier color aleatorio, por tanto podría ser cualquier valor entre 0 (RGB[0,0,0] - negro) y 0xFFFFFF (RGB[255,255,255] - blanco).
* Crea una brocha con un color aleatorio
loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF)))
EJEMPLO 4 - DIBUJAR ALGÚN TEXTO EN UN FONDO CON TRAMA COLOREADA UTILIZANDO COLORES ALEATORIOS CON ROTACIÓN DE CARACTERES.
** Generador de imágenes Captcha
** Crea una imagen muy sencilla que contiene una cadena
** Cada carácter en color aleatorio diferente y una ligera rotación
** Se basa en el código de Rick Strahl expuesto en su weblog
** http://www.west-wind.com/wconnect/weblog/ShowEntry.blog?id=556
** El siguiente ejemplo muestra como crear un sencillo Generador de imágenes Captcha
LOCAL lcText AS Character
lcText = CreateString(8)
_SCREEN.AddProperty("System", NEWOBJECT("xfcSystem", ;
LOCFILE("system.vcx","vcx")))
LOCAL loBmp AS xfcBitmap
LOCAL loFont AS xfcFont
LOCAL loSizeF AS xfcSizeF
LOCAL loScreenGfx AS xfcGraphics
LOCAL loGfx AS xfcGraphics
WITH _SCREEN.System.Drawing
loScreenGfx = .Graphics.FromHwnd(_Screen.HWnd)
loFont = .Font.New("Tahoma",20) && Font Name, Size
* Obtiene la altura y el ancho necesarios para la cadena
loSizeF = loScreenGfx.MeasureString(lcText, loFont)
LOCAL lnWidth, lnHeight, lnCharWidth
lnWidth = loSizeF.Width * 1.5 && Se estrecha al 50% para garantizar que quepan los caracteres rotados
lnHeight = loSizeF.Height
lnCharWidth = lnWidth / LEN(lcText)
* Crea un bitmap nuevo
loBmp = .Bitmap.New(lnWidth, lnHeight)
* Obtiene un objeto Graphics para dibujar
loGfx = .Graphics.FromImage(loBmp)
loGfx.Clear(.Color.White)
* Llena el fondo del rectángulo con una brocha sólida de colores pastel aleatorios
loGfx.FillRectangle( ;
.Drawing2D.HatchBrush.New(INT(RAND()*52), ;
.Color.FromARGB(255, INT(RAND() * 76)+180, INT(RAND() * 76)+180, INT(RAND() * 76)+180), ;
.Color.FromARGB(255, INT(RAND() * 76)+180, INT(RAND() * 76)+180, INT(RAND() * 76)+180)), ;
0,0,loBmp.Width,loBmp.Height)
* Dibuja cada carácter en un color aleatorio diferente
* y un ángulo aleatorio de rotación desde -40 a +40 grados
LOCAL n, lcChar
LOCAL loBrush AS xfcSolidBrush
LOCAL loMatrix AS xfcMatrix
FOR n = 0 TO LEN(lcText)
lcChar = SUBSTR(lcText, n, 1)
* Crea una brocha con un color aleatorio
loBrush = .SolidBrush.New(.Color.FromRGB(INT(RAND() * 0xFFFFFF)))
* Crea una matriz para aplicar la rotación de los caracteres
loMatrix = _Screen.System.Drawing.Drawing2D.Matrix.New()
* Calcula el ángulo aleatorio
lnAngle = INT(RAND() * 80) - 40
* Rota al centro de cada posición de carácter
loMatrix.RotateAt(lnAngle, ;
.Point.New((n-1) * lnCharWidth + (lnCharWidth / 2), lnHeight / 2))
* Asocia la matriz al objeto Graphics
loGfx.Transform = loMatrix
* Dibuja el carácter
loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)
ENDFOR
* Guarda la imagen en disco
loBmp.Save("c:\captcha4.png", .Imaging.ImageFormat.Png)
ENDWITH
* Muestra la imagen en el explorador
RUN /n explorer.exe c:\captcha4.png
RETURN
PROCEDURE CreateString(tnLength)
LOCAL lcText, lcChar, x, n
lcText = ""
FOR n = 1 TO tnLength
x = INT(RAND() * 36)
lcChar = IIF(x < 10, TRANSFORM(x), CHR(x + 55))
lcText = lcText + lcChar
ENDFOR
RETURN lcText
ENDPROC
Aquí hicimos lo mismo; pero utilizando una brocha con trama para el fondo, y aplicando una ligera rotación de cada carácter. La rotación de caracteres ya se ha discutido en un escrito anterior http://weblogs.foxite.com/vfpimaging/2012/03/16/draw-rotated-strings-with-gdiplusx en cualquier caso, he aquí una corta explicación:
"System.Drawing" brinda un método que permite dibujar cualquier objeto (forma, texto y otra imagen) utilizando rotación, de cualquier ángulo. Para esto necesitamos crear un objeto Matriz, y utilizamos el método "RotateAt", pasando el punto central del objeto que se rotará. En este caso, necesito pasar el centro de la cadena.
* Crea una matriz para aplicar la rotación de los caracteres
loMatrix = .Drawing2D.Matrix.New()
* Calcula el ángulo aleatorio
lnAngle = INT(RAND() * 80) - 40
* Rota al centro de cada posición de carácter
loMatrix.RotateAt(lnAngle, ;
.Point.New((n-1) * lnCharWidth + (lnCharWidth / 2), lnHeight / 2))
* Asocia la matriz al objeto Graphics
loGfx.Transform = loMatrix
* Dibuja el carácter
loGfx.DrawString(lcChar, loFont, loBrush, (n-1) * lnCharWidth, 0)
¿QUÉ MÁS?
Podemos hacer algunas otras cosas, tales como dibujar algunas líneas aleatorias en la imagen. Una forma podría ser simplemente agregando este sencillo código inmediatamente antes de guardar la imagen:
LOCAL x1, y1, x2, y2, lnColor
FOR n = 1 TO 10
x1 = RAND() * lnWidth
x2 = RAND() * lnWidth
y1 = RAND() * lnHeight
y2 = RAND() * lnHeight
lnColor = RAND() * 0xFFFFFF
loGfx.DrawLine(.Pen.New(.Color.FromRGB(lnColor),1),x1, y1, x2, y2)
ENDFOR
A pesar de que se conoce que estos son únicamente algunos CAPTCHA decoradores, puede ser muy útil en determinadas situaciones. Mi objetivo en este escrito es simplemente mostrar algunas técnicas para crear buenos captchas. Hay mucho que mejorar. Usted puede utilizar fuentes irregulares aleatorias, otros fondos, aplicar algunas transformaciones a caracteres, como escalar, cortes, nublados, efectos halo. ¡ Todo esto con GDI+ !
En la próxima liberación de la biblioteca, espero enviar algunos ejemplos que creen efectos interesantes en textos, tales como, algunos que se muestran debajo pudieran también ser útiles.
Manténgase en contacto.