GcaptchaZ/gcaptchaz.c

257 lines
6.6 KiB
C

#include <sys/time.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <gd.h>
#include <gdfontl.h>
/*
This code is only a C implementation (with some minor modifications) of this captcha generator :
http://logz.org/captchaz/
Copyright November 2012 Grégory Soutadé
Copyright July 2007 Loz
LICENCE : Copyleft, Licence Art Libre (http://artlibre.org/licence/lal)
Modifications :
* Group letters
* Add another layer for letter rendering (more confusing)
* Change start, not appropriate for big fonts
* Change min/max font size
You need libgd2-xpm-dev package
It's recommanded the included font "scrawl.ttf"
*/
#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
// If not defined
/* int overflow2(int a, int b) */
/* { */
/* if(a < 0 || b < 0) { */
/* fprintf(stderr, "gd warning: one parameter to a memory allocation multiplication is negative, failing operation gracefully\n"); */
/* return 1; */
/* } */
/* if(b == 0) */
/* return 0; */
/* if(a > 0xFFFFFFFF / b) { */
/* fprintf(stderr, "gd warning: product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully\n"); */
/* return 1; */
/* } */
/* return 0; */
/* } */
static int my_rand(int min, int max)
{
int val;
struct timeval tv;
static unsigned int seed = 0;
int interval;
if (seed == 0)
{
gettimeofday(&tv, NULL);
seed = tv.tv_usec;
}
val = rand_r(&seed);
if (min < 0)
{
if (max < 0)
interval = -(min-max);
else
interval = max-min;
}
else
interval = max-min;
val = (int)(((double)val/(double)RAND_MAX)*(double)(interval));
val += min;
/* printf("rand [%d..%d] %d\n", min, max, val); */
return val;
}
static inline int random_color(gdImagePtr im, int a, int b) {
return gdImageColorAllocate(im, my_rand(a,b), my_rand(a,b), my_rand(a,b));
}
static int generate_captcha(int width, int height, int inverse, char* font, char* phrase, char* out)
{
gdImagePtr im;
int R, c1, c2;
int wd, x, y, w1, w2, limit, n, p, r, g, b;
int size, half, len;
double rotation;
char letter[2] = {0, 0};
int brect[8];
char* error;
const double rad = 3.14/(double)180;
// initialize in-memory image with gd library
im = gdImageCreateTrueColor(width, height);
R = (inverse) ? 0xFF : 0x00;
gdImageFilledRectangle(im, 0, 0, width, height, random_color(im, 222^R, 255^R));
c1 = my_rand(150^R, 185^R);
c2 = my_rand(195^R, 230^R);
// encolour bg
wd=20; x=0;
while (x < width)
{
gdImageFilledRectangle(im, x, 0, x+wd, height, random_color(im, 222^R, 255^R));
x += wd;
wd += MAX(10, my_rand(0, 20) - 10);
}
// make interesting background I, lines
wd=4; w1=0; w2=0;
for(x=0; x<width; x+=wd)
{
if (x < width)
gdImageLine(im, x+w1, 0, x+w2, height-1, random_color(im, c1, c2));
if (x < height)
gdImageLine(im, 0, x-w2, width-1, x-w1, random_color(im, c1, c2));
wd += my_rand(0, 8)-4;
if (wd < 1) wd = 2;
w1 += my_rand(0, 8)-4;
w2 += my_rand(0, 8)-4;
if ((x > height) && (x > height)) // ???
break;
}
// more disturbing II, random letters
limit = my_rand(30, 90);
for(n=0; n<limit; n++)
{
letter[0] = my_rand(31, 125); // only one letter
size = my_rand(5, height/2);
half = size / 2;
x = my_rand(-half, width+half);
y = my_rand(half, height);
rotation = my_rand(60, 300);
c1 = random_color(im, 130^R, 240^R);
error = gdImageStringFT(im, brect, c1, font, size, rotation, x, y, letter);
if (error)
{
fprintf(stderr, "%s, font is %s\n", error, font);
return 1;
}
}
// add the real text to it
len = strlen(phrase);
w1 = 10;
w2 = width / (height/4); // Change start, not appropriate for big fonts
x = w2;
for(p=0; p<len; p++)
{
letter[0] = phrase[p];
size = my_rand(height/4, height/3); // Change min/max font size
half = size / 2;
rotation = (double)my_rand(-33, 33)*rad;
y = my_rand(size+3, height-3);
/* x = w1 + w2*p; */
w1 += my_rand(-width/90, width/40);
r = my_rand(30, 99) ; g = my_rand(30, 99) ; b = my_rand(30, 99);
c1 = gdImageColorAllocate(im, r*1^R, g*1^R, b*1^R);
c2 = gdImageColorAllocate(im, r*2^R, g*2^R, b*2^R);
gdImageStringFT(im, brect, c2, font, size, rotation, x+(size/40), y, letter);
gdImageStringFT(im, brect, c1, font, size, rotation, x, y-(size/40), letter);
// Add another layer
gdImageStringFT(im, brect, c1, font, size, rotation, x+(size/10), y+(size/10), letter);
// Group letters
x += size;
}
FILE* f= fopen(out, "wb");
if (!f)
{
fprintf(stderr, "Error opening %s\n", out);
return 1;
}
gdImagePng(im, f);
fclose(f);
gdImageDestroy(im);
return 0;
}
static void usage()
{
printf("gcpatchaz is a command line captcha generator\n\n");
printf("usage : gcpatchaz [options]\n");
printf("\t-w WIDTH\n");
printf("\t-h HEIGHT\n");
printf("\t-o OUT.PNG\n");
printf("\t-f FONT\n");
printf("\t-s STRING\n");
printf("\t-i : inverse\n");
printf("\t-h : help\n");
printf("\nLICENCE : Copyleft, Licence Art Libre (http://artlibre.org/licence/lal)\n");
printf("Copyright Grégory Soutadé (2012)\n");
printf("Copyright Loz (2007)\n");
}
int main(int argc, char** argv)
{
int opt;
int width=180, height=40;
char* out = "captcha.png", *font = "ttf-japanese-gothic.ttf", *string = NULL;
int inverse= 0;
int i, res;
while ((opt = getopt(argc, argv, "w:H:o:f:s:ih")) != -1)
{
switch(opt)
{
case 'w': width = atoi(optarg); break;
case 'H': height = atoi(optarg); break;
case 'o': out = optarg; break;
case 'f': font = optarg; break;
case 's': string = strdup(optarg); break;
case 'i': inverse = 1; break;
case 'h':
usage();
return 0;
default:
usage();
return 1;
}
}
if (string == NULL)
{
string = malloc(6);
for(i=0; i<5; i++)
{
string[i] = my_rand(65, 90);
}
string[i] = 0;
}
res = generate_captcha(width, height, inverse, font, string, out);
if (res == 0)
printf("Captcha generated for text \"%s\"\n", string);
free(string);
return 0;
}