CAPTCHA, the simplest gst external module

Tagged:  •    •    •    •  

This is a translation of http://d.hatena.ne.jp/hdb/20071201 which is a blog post in Japanese by Hiroshi Higa. I don't speak Japanese, so I asked the author to write a very simple summary of the article, which I then augmented.

Basically, his idea is to implement a captcha. There are good captcha libraries for Squeak, e.g. SW2Captcha which uses Morphic to generate images. That may sound like using a bazooka to shoot an ant, but it's actually quite nice and the code is interesting. In GNU Smalltalk, to do the same you would need GD bindings — note to self, put that on the todo list!

Hiroshi thought that he just wanted to tech himself a bit of FFI, so he would do a bigger part of the job in C. This approach may be less extensible, less OO, less everything, but it also provides a very basic but pratical example of FFI, and made his post simple and easy to understand.

In GNU Smalltalk there are basically two ways to do dynamic linking: libraries and modules. With libraries, every time you create an FFI call the system checks every library you added for an implementation of the function. With modules, the module itself tells GNU Smalltalk what functions it provides.

Hiroshi's module is basically a single C function, plus the code to tell GNU Smalltalk about that function's existence:

#include <stdio.h>
#include "gstpub.h"
#include "gd.h"

static VMProxy *vmProxy;

int imageString(char *string, char *fontname, unsigned char *fg, unsigned char *bg, double ptsize, char *filename)
{
  gdImagePtr image;
  int background, foreground;
   int brect[8];
  int x, y;
  char *err;
  FILE *out;

  err = gdImageStringFT(NULL, &brect[0], 0, fontname, ptsize, 0.0, 0, 0, string);
  if (err) {return 1;}

  x = brect[2] - brect[6] + 6;
  y = brect[3] - brect[7] + 6;
  image = gdImageCreate(x, y);

  background = gdImageColorResolve(image, bg[0], bg[1], bg[2]);
  foreground = gdImageColorResolve(image, fg[0], fg[1], fg[2]);

  x = 3 - brect[6];
  y = 3 - brect[7];
  err = gdImageStringFT(image, &brect[0], foreground, fontname, ptsize, 0.0, x, y, string);
  if (err) {return 1;}

  out = fopen(filename, "wb");
  gdImagePng(image, out);

  fclose(out);

  gdImageDestroy(image);
  return 0;
}

void gst_initModule(VMProxy * proxy)
{
  vmProxy = proxy;
  vmProxy->defineCFunc("imageString", imageString);
}

In more complex examples, you would use vmProxy to access and create Smalltalk objects. But FFI allows you to pass Smalltalk objects as C types in a very natural way.

Compiling the module is somewhat a mess. If you want to do it portable you do it with libtool (see Creating and distributing packages on the wiki for more info) but for Linux you just need this:

gcc -shared -Wl,-soname,gd.so.1 -lgd -lpng -lz -ljpeg \
  -lfreetype -ldl -lm -o gd.so gd.o

Put the file in your LD_LIBRARY_PATH or under /usr/lib/smalltalk. Or you can leave it in the current directory and run GNU Smalltalk with

SMALLTALK_MODULES=. gst

Here is the Smalltalk side of the module:

Object subclass: GD [
    GD class >> imageString: aString font: fontPath 
        foreground: fg background: bg size: size to: file [
        <cCall: 'imageString' returning: #int
            args: #(#string #string #byteArray
                    #byteArray #double #string)>
    ]
]

And here is how you test your module. First, we load the module and file in the package. You can also make a .star file and a nice package following the instructions in the wiki link given above:

DLD addModule: 'GD'.
FileStream fileIn: 'GST_DIR/share/smalltalk/GD/GD.st'.

"A useful method... (will be in 3.0.1)"
SequenceableCollection extend [
    atRandom [
        ^self at: (Random between: 1 and: self size)
    ]
]

"Make a four character captcha."
fontPath := '/usr/share/fonts/bitstream-vera/Vera.ttf'.
authChars := '0123456789'.
authChars := authChars, 'abcdefghijklmnopqrstuvwxyz'.
authChars := authChars, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.

authString := String streamContents: [ :s |.
   4 timesRepeat: [ s nextPut: authChars atRandom ]].

GD
    imageString: authString
    font: fontPath
    foreground: #[255 255 255]
    background: #[0 0 0]
    size: 40
    to: 'captcha.png'.

That's it.

Howdy! I basically would like to give a huge thumbs up for the good data you've got here on this post. I will probably be coming once again to your weblog for far more soon. dffeabeakbdk

Where To Buy Cheap Generic Cefuroxime 250mg in Baltimore kfkkddfaceke

Hahahahahahaha, this politics related YouTube video is actually so comic, I liked it. Thanks in support of sharing this.

Over the course of the initial period, they began to flrm group identities. kdceaacfdfea

I see a couple of mistakes:

- The definition of the method in class GD seems to be missing the "cCall ..." part (oh, I see, it also disappeared from my comment).
- The method is defined as an instance method but the example sends it to the class.
- The first parameter to imageString should be "authString".

Thanks for the example, I'll be back with a million questions :D

Thanks, I addressed all your comments.

User login