GD::Se...tyImage(3pm)

πŸŽ€ GD::SecurityImage πŸŽ€

okay bestie, welcome to the GD::SecurityImage page β€” from User Contributed Perl Documentation, and it is giving captcha generator, no cap. this is part one of three so buckle up, we're going in order, and we are NOT skipping a single crumb πŸ’…


✨ NAME ✨

GD::SecurityImage β€” Security image (captcha) generator. that's the whole name, that's the whole job. periodt 🫢

🌸 VERSION 🌸

version 1.75 β€” that's the one, that's the vibe check result ⭐

πŸ¦‹ SYNOPSIS πŸ¦‹

so like, first thing you do is pull the module in:

   use GD::SecurityImage;

then, the classic move β€” a normal, non-fancy image:

   # Create a normal image
   my $image = GD::SecurityImage->new(
                  width   => 80,
                  height  => 30,
                  lines   => 10,
                  gd_font => 'giant',
               );
      $image->random( $your_random_str );
      $image->create( normal => 'rect' );
   my($image_data, $mime_type, $random_number) = $image->out;

orrrrr (the plot twist) β€” you can use an external ttf font instead:

   # use external ttf font
   my $image = GD::SecurityImage->new(
                  width    => 100,
                  height   => 40,
                  lines    => 10,
                  font     => "/absolute/path/to/your.ttf",
                  scramble => 1,
               );
      $image->random( $your_random_str );
      $image->create( ttf => 'default' );
      $image->particle;
   my($image_data, $mime_type, $random_number) = $image->out;

or, and hear me out, you can just be lazy in the most iconic way possible β€” most of the public methods can be chained:

   my($image, $type, $rnd) = GD::SecurityImage->new->random->create->particle->out;

and that one-liner right there makes a security image with the default settings. but bestie… that may not be useful. just saying bestie, defaults are defaults 😭

if you go the require route instead of use, you must import it yourself:

   require GD::SecurityImage;
   GD::SecurityImage->import;

now, plot twist number two: this module also supports Image::Magick, but the default interface is the GD module. to unlock Image::Magick support, you gotta call the module with the use_magick option:

   use GD::SecurityImage use_magick => 1;

and if you require the module (instead of use), you still gotta import it, just with the option this time:

   require GD::SecurityImage;
   GD::SecurityImage->import(use_magick => 1);

🫢 okay important side note: the module does not export anything actually. like, nothing, zero, nada. but import is the thing that loads the necessary sub modules. and if you don' t import, the required modules will not be loaded and probably, you'll die(). that's not a drill, that's a whole tragedy πŸ’€

πŸ‘‘ DESCRIPTION πŸ‘‘

the (so called) "Security Images" are so popular. most internet software use these in their registration screens to block robot programs (which may register tons of fake member accounts, honestly rude of them). Security images are basicaly, graphical CAPTCHAs β€” and that stands for Completely Automated Public Turing test to Tell Computers and Humans Apart. iconic acronym, love that for us ✨

this module gives you a basic interface to create such an image. the final output is the actual graphic data, the mime type of the graphic and the created random string. and it also has some "styles" that are used to create the background (or foreground) of the image, so you can serve looks πŸŽ€

if you are an Authen::Captcha user, go peek at GD::SecurityImage::AC for migration from Authen::Captcha to GD::SecurityImage. glow-up arc, fr fr 🌈

⚠️ also super important: this module is just an image generator. not a captcha handler. the validation of the generated graphic is left to your programming taste, bestie β€” that part is on you 🫡

but there are some captcha handlers for several Perl FrameWorks. if you are an user of one of these frameworks, see "GD::SecurityImage Implementations" in the "SEE ALSO" section for information. iykyk πŸ‘‘


πŸ’– NAME πŸ’–

(lowkey we're doing this one again, and it hits the same every time)

GD::SecurityImage β€” Security image (captcha) generator. πŸ’…

🌈 COLOR PARAMETERS 🌈

this module can use both RGB and HEX values as the color parameters. HEX values are recommended, since they are widely used and recognised. taste! ✨

   $color  = '#80C0F0';     # HEX
   $color2 = [15, 100, 75]; # RGB
   $i->create($meth, $style, $color, $color2)

   $i->create(ttf => 'box', '#80C0F0', '#0F644B')

RGB values must be passed as an array reference including the three Red, Green and Blue values. three values, array ref, that's the whole recipe πŸ“

Color conversion is transparent to the user β€” you don' t gotta do anything, it just happens. you can use hex values under both GD and Image::Magick, and they' ll be automagically converted to RGB if you are under GD. automagically. we love to see it πŸ’œ

ok that's part 1 bestie, there's more coming so don't go anywhere πŸ¦‹

πŸ’– METHODS πŸ’–

new β€” the constructor, the origin story ✨

new() takes a bunch of arguments. Not a fan of reading a wall of text? Same bestie, same. Here they are, one by one, with zero skips:

  • width β€” the width of the image, in pixels. That's it, that's the whole vibe. πŸ“
  • height β€” the height of the image, in pixels. Iconic duo with width. πŸ“
  • ptsize β€” a numerical value. It's the point size of the ttf character. You ONLY need this if you wanna use a ttf font in your image, otherwise it's just sitting there being extra. πŸŽ€
  • lines β€” how many lines you'll see chillin' in the background of the image. The alignment can be vertical, horizontal, angled, or all of them at once (overachiever behavior 🫢). Crank this value up and your image gets more cryptic β€” lowkey the glow-up you want for a security code.
  • font β€” the absolute path to your TrueType (.ttf) font file. Heads up bestie: relative font paths are NOT recognized, and that's not us being dramatic β€” it's problems in the libgd library, not our fault. 😭

If you're 100% sure you set this to a correct value and you're still getting warnings or an empty image, check that your path has no spaces in it. Looks like libgd also has problems with these kinds of paths (eg: '/Documents and Settings/user' under Windows). The audacity, honestly.

Set this parameter if you want to use ttf in your image.

  • gd_font β€” set this if you want the default interface. The recognized values are "Small", "Large", "MediumBold", "Tiny", "Giant". The names are case-insensitive, so you can pass lower-cased parameters and nobody will judge you. πŸ’…
  • bgcolor β€” the background color of the image. The canvas, the moment, the whole aesthetic. 🌈
  • send_ctobg β€” if this is a true value, the random security code will be displayed in the background and the lines will pass over it. (send_ctobg = send code to background, so the name literally ate.) πŸ‘‘
  • frame β€” if this is a true value, a frame gets added around the image. This option is enabled by default, so it's already slaying unless you say otherwise.
  • scramble β€” if set, the characters get scrambled. If you turn this on, be sure to use a wider image, because the characters will be separated with three spaces. Give them room to breathe fr fr. πŸ¦‹
  • angle β€” sets the angle for scrambled/normal characters. Careful though: if you pass an "angle" parameter, the characters in your random string will have a fixed angle. If you do NOT set an "angle" parameter, the angle(s) will be random. Chaos mode: auto-enabled. 🎲

When the scramble option is not enabled, this parameter still controls the angle of the text. BUT β€” since the text will be centered inside the image, using this parameter without the scramble option will require a taller image. Clipping will occur with smaller height values. No cap, it will clip. βœ‚οΈ

Unlike the GD interface, "angle" is in "degree"s and can take values between 0 and 360.

  • thickness β€” sets the line drawing width. Takes numerical values. Default values are 1 for GD and 0.6 for Image:Magick.
  • rndmax β€” the minimum length of the random string. Default value is 6. πŸ”’
  • rnd_data β€” the default character set used to create the random string is 0..9. But if you want to use letters too, you can set this parameter. It takes an array reference as the value. πŸ’Œ

Not necessary, and it will not be used if you pass your own random string.


random 🎰

Creates the random security string, OR sets the random string to the value you passed in. If you pass your own random string, be aware that it must be at least six (defined in "rndmax") characters long. Short codes will not be tolerated bestie, the module said what it said.

random_str 🎁

Returns the random string. Must be called after random(). Order matters, it's a sequence not a vibe. ✨


create 🎨

This method creates the actual image. It takes four arguments, but none of them are mandatory β€” so you can just call it and hope for the best, or go full custom.

$image->create($method, $style, $text_color, $line_color);

$method can be "normal" or "ttf".

$style can be one of the following:

  • default β€” The default style. Draws horizontal, vertical and angular lines. The full trio. πŸ’«
  • rect β€” Draws horizontal and vertical lines.
  • box β€” Draws two filled rectangles.

The "lines" option passed to new controls the size of the inner rectangle for this style. If you increase the "lines", you'll get a smaller internal rectangle. Using smaller values like 5 can be better.

  • circle β€” Draws circles.
  • ellipse β€” Draws ellipses.
  • ec β€” This is the combination of ellipse and circle styles. Draws both ellipses and circles. Two icons, zero compromise. 🌸
  • blank β€” Draws nothing. See "OTHER USES" for what that's about.

You can use this code to get all available style names:

my @styles = grep {s/^style_//} keys %GD::SecurityImage::Styles::;

The last two arguments ($text_color and $line_color) are the colors used in the image β€” text and line color, respectively:

$image->create($method, $style, [0,0,0], [200,200,200]);
$image->create($method, $style, '#000000', '#c8c8c8');

particle ✨

Must be called after create. (Timing, bestie, timing.)

Adds random dots to the image. They'll cover all over the surface β€” full coverage, no bare patches. It accepts two parameters: the density (number) of the particles, and the maximum number of dots around the main dot.

$image->particle($density, $maxdots);

The default value of $density is dependent on your image's width or height value. The greater value of width and height is taken and multiplied by twenty. So if your width is 200 and height is 70, $density is "200 * 20 = 4000" (unless you pass your own value). Big number alert: the default value of $density can be too much for smaller images. 🚨

$maxdots defines the maximum number of dots near the default dot. Default value is 1. If you set it to 4, the selected pixel and 3 other pixels near it will be used and colored.

The color of the particles is the same as the color of your text (defined in create). Matching set, we love consistency. 🫢


info_text πŸ“

This method must be called after create. If you call it early, you'll die. Not metaphorically, it will literally die on you. πŸ’€

"info_text" adds an extra text to the generated image. You can also put a strip under the text. The purpose of this method is to display additional information on the image β€” copyright information can be an example for that.

$image->info_text(
   x      => 'right',
   y      => 'up',
   gd     => 1,
   strip  => 1,
   color  => '#000000',
   scolor => '#FFFFFF',
   text   => 'Generated by GD::SecurityImage',
);

Options:

  • x β€” Controls the horizontal location of the information text. Can be either "left" or "right". β¬…οΈβž‘οΈ
  • y β€” Controls the vertical location of the information text. Can be either "up" or "down". ⬆️⬇️
  • strip β€” If this has a true value, a strip will be added to the background of the information text. The little banner moment. πŸŽ€
  • gd β€” This option can only be used under "GD". Has no effect under Image::Magick. If it has a true value, the standard GD font "Tiny" will be used for the information text.

If this option is not present or has a false value, the TTF font parameter passed to "new" will be used instead.

  • ptsize β€” The ptsize value of the information text to be used with the TTF font. The TTF font parameter can not be set with info_text(). The value passed to new() will be used instead.
  • color β€” The color of the information text.
  • scolor β€” The color of the strip. 🎨
  • text β€” This parameter controls the displayed text. If you want to display long texts, be sure to adjust the image, or clipping will occur. Customize responsibly.

out πŸ“€

This method finally returns the created image, the mime type of the image, and the random number(s) generated. The big reveal. 🌟

The returned mime type is "png" or "gif" or "jpeg" for "GD", and "gif" for "Image::Magick" (if you do not "force" some other format).

"out" method accepts arguments:

@data = $image->out(%args);
  • force β€” You can set the output format with the "force" parameter:
@data = $image->out(force => 'png');

If "png" is supported by the interface (via "GD" or "Image::Magick"), you'll get a png image. If the interface does not support this format, the out() method will use its default configuration. It tried its best, okay? πŸ₯Ί

  • compress β€” And with the "compress" parameter, you can define the compression for "png" and quality for "jpeg":
@data = $image->out(force => 'png' , compress => 1);
@data = $image->out(force => 'jpeg', compress => 100);

When you use "compress" with "png" format, the value of "compress" is ignored and it is only checked if it has a true value. With "png" the compression will always be 9 (maximum compression). eg:

@data = $image->out(force => 'png' , compress => 1);
@data = $image->out(force => 'png' , compress => 3);
@data = $image->out(force => 'png' , compress => 5);
@data = $image->out(force => 'png' , compress => 1500);

All will default to 9. But this one will disable compression:

@data = $image->out(force => 'png' , compress => 0);

But the behaviour changes if the format is "jpeg" β€” the value of "compress" will be used for "jpeg" quality, which is in the range 1..100.

Compression and quality operations are disabled by default. πŸ™ˆ


raw 🧬

Depending on your usage of the module, returns the raw "GD::Image" object:

my $gd = $image->raw;
print $gd->png;

or the raw "Image::Magick" object:

my $magick = $image->raw;
$magick->Write("gif:-");

Can be useful if you want to modify the graphic yourself β€” DIY era, we respect it. πŸ’… If you want to get an image type, see the "force" option in "out".


gdbox_empty, add_strip, cconvert, gdf, h2r, is_hex, r2h, random_angle 🫧

For gdbox_empty: see "path bug" in "GD bug" for usage and other information on this method.

The rest β€” add_strip, cconvert, gdf, h2r, is_hex, r2h, random_angle β€” the page just lists their names right here with nothing attached, so that's exactly what you're getting. No invented lore, bestie, I don't make things up. 😌


🫢 UTILITY METHODS 🫢

backends πŸ—‚οΈ

Returns a list of available GD::SecurityImage back-ends.

my @be = GD::SecurityImage->backends;

or

my @be = $image->backends;

If called in a void context, it prints a verbose list of available GD::SecurityImage back-ends:

Available back-ends in GD::SecurityImage v1.55 are:
        GD
        Magick

Search directories:
           /some/@INC/dir/containing/GDSI

You can see the output with this command:

perl -MGD::SecurityImage -e 'GD::SecurityImage->backends'

or under windows:

perl -MGD::SecurityImage -e "GD::SecurityImage->backends"

(Yes, the quote situation changes on Windows. Siblings that slay differently. πŸͺŸβœ¨)


πŸ’– EXAMPLES πŸ’–

Wanna see it in action? Peep the tests in the distribution, bestie. Also go look at the demo program eg/demo.pl, which shows off an Apache::Session implementation of GD::SecurityImage. It's giving ✨working example✨.

Grab the distribution from a CPAN mirror near you if you don't already have the files. Downloading is self-care, fr fr.

And heads up: running the test suite will also create some sample images for you. Free samples, no cap. πŸŽ€

πŸ¦‹ OTHER USE CASES πŸ¦‹

Okay so the tea is that GD::SecurityImage's drawing capabilities aren't only for captchas β€” you can also use them for counter image generation, or just to display whatever arbitrary messages your heart desires. Iconic, honestly:

use CGI qw(header);
use GD::SecurityImage 1.64; # we need the "blank" style

my $font  = "StayPuft.ttf";
my $rnd   = "10.257"; # counter data

my $image = GD::SecurityImage->new(
   width  =>   140,
   height =>    75,
   ptsize =>    30,
   rndmax =>     1, # keeping this low helps to display short strings
   frame  =>     0, # disable borders
   font   => $font,
);

$image->random( $rnd );
# use the blank style, so that nothing will be drawn
# to distort the image.
$image->create( ttf => 'blank', '#CC8A00' );
$image->info_text(
   text   => 'You are visitor number',
   ptsize => 10,
   strip  =>  0,
   color  => '#0094CC',
);
$image->info_text(
   text   => '( c ) 2 0 0 7   m y s i t e',
   ptsize => 10,
   strip  =>  0,
   color  => '#d7d7d7',
   y      => 'down',
);

my($data, $mime, $random) = $image->out;

binmode STDOUT;
print header -type => "image/$mime";
print $data;

😭 ERROR HANDLING 😭

So sometimes things go wrong and die gets called in some methods if something fails. That means you may need to eval your code to catch those exceptions. Wrap it up, bestie, protect your runtime. 🫢


πŸ’… TIPS πŸ’…

Okay this is important so listen up. If you look at the demo program (and not just look at it β€” actually try running it) you'll see that the random code changes after every request, whether it succeeded or not. That's not a quirk, that's the whole point.

Here's the mistake you must NOT make: if you don't change the random code after a failed request, and you display that random code inside your HTML (like Wrong! It must be <random>), then that's a straight-up logical mistake. Why? Because now the user (or a robot πŸ’€) can just copy & paste that random code into your validator without ever looking at the security image and pass your test. The audacity. Just don't do that. Random code must change after every validation. Periodt.

If you wanna be a little more strict πŸ‘‘, you can also add a timeout key to the session β€” heads up, this feature currently does not exits in the demo β€” and expire the related random code after the timeout. Reason being: robots can call the image generator directly, without even needing the HTML form, so they can sit there examining the image for a while without it ever changing. A timeout implemetation may prevent that. Stay safe out there. 🌸


πŸ› BUGS πŸ›

Got a bug or a request to report? Skim the SUPPORT section first, bestie.

🌈 GD bug 🌈

path bug, bebe. 🫠

libgd and GD.pm are not vibing with relative paths, nor with paths that have spaces in them. So if you pass a font path that isn't an exact path, or a path with a space in it, you may end up with an empty image. Rude, but that's the situation.

To check whether the module failed to find the ttf font (when you're using GD), a new method was added: gdbox_empty(). And it has to be called after create():

$image->create;
die "Error loading ttf font for GD: $@" if $image->gdbox_empty;

One little caveat: gdbox_empty() always returns false if you're using Image::Magick. No cap.


πŸ’€ COMMON ERRORS πŸ’€

Wrong GD installation

So I've gotten error reports saying that GD::SecurityImage dies with this error:

Can't locate object method "new" via package "GD::Image"
(perhaps you forgot to load "GD::Image"?) at ...

This is because of a wrong installation of the GD module, bestie. GD includes XS code and it needs to be compiled. You can't just copy/paste the GD.pm and expect it to work. It will not. Like, at all.

If you're on Windows and don't have a C compiler, you have to add new repositories to install GD, since ActiveState's own repositories don't include GD. Randy Kobes and J-L Morel have ppm repositories for both 5.6.x and 5.8.x and both of them have GD:

http://www.bribes.org/perl/ppmdir.html http://theoryx5.uwinnipeg.ca/

bribes.org also has a GD::SecurityImage ppd, so you can literally just install GD::SecurityImage straight from that repository. Slay. ✨

libgd errors

There are some issues related to wrong/incomplete compiling of libgd, plus old/new version conflicts. Messy, but here's the breakdown:

libgd without TTF support

If your libgd is compiled without TTF support, you'll get an empty image. The lines will be drawn, but there will be no text. The whole vibe is ruined. You can check for this with the gdbox_empty method.

GIF - Old libgd or libgd without GIF support enabled

If your GD has a gif method, but you're getting empty images out of the gif() method, you have to update your libgd or compile it with GIF enabled. That's the fix, fr fr.

You can test if gif is working from the command line:

perl -MGD -e '$_=GD::Image->new;$_->colorAllocate(0,0,0);print$_->gif'

or under windows:

perl -MGD -e "$_=GD::Image->new;$_->colorAllocate(0,0,0);print$_->gif"

Conclusions, bestie:

  • If it dies, your GD is very old. 😭
  • If it prints nothing, your libgd was compiled without GIF enabled (upgrade or re-compile).
  • If it prints out a junk that starts with 'GIF87a', everything is OK. πŸ‘‘

πŸ‘‘ CAVEAT EMPTOR πŸ‘‘

  • Using the default library GD is a better choice. Since it is faster and does not use that much memory, while Image::Magick is slower and uses more memory.
  • The internal random code generator is used only for demonstration purposes for this module. It may not be effective. You must supply your own random code and use this module to display it.

🌈 SEE ALSO 🌈

Other CAPTCHA Implementations & Perl Modules

GD::SecurityImage Implementations

  • GD::SecurityImage::AC: Authen::Captcha drop-in replacement module.
  • Sledge::Plugin::Captcha
  • Catalyst::Plugin::Captcha
  • CGI::Application::Plugin::CAPTCHA
  • Angerwhale::Controller::Captcha

πŸ’œ AUTHOR πŸ’œ

Burak Gursoy <burak@cpan.org>


This software is copyright (c) 2004 by Burak Gursoy.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


And that's a wrap on the whole page, bestie!! 🫢✨ This one's perl v5.38.2, dated 2024-03-06, and it lives under GD::Se...tyImage(3pm) in your friendly neighborhood User Contributed Perl Documentation. Go forth and captcha responsibly. No cap, ily, bye πŸ’–

πŸ“¦ where this page comes from