burak / CPAN-GD-SecurityImage
Security image (captcha) generator for Perl
$ hg clone http://bitbucket.org/burak/cpan-gd-securityimage/
| commit 15: | 4158229bf536 |
| parent 14: | 5d9082a75277 |
| branch: | default |
- View burak's profile
-
burak's public repos »
- CPAN-Padre-Plugin-HG
- CPAN-Net-Bitbucket
- CPAN-GD-SecurityImage
- CPAN-Lingua-TR-Numbers
- CPAN-Sys-Info-Driver-Windows
- CPAN-Task-Lingua-Any-Numbers
- CPAN-Time-Elapsed
- CPAN-Win32-ASP-CGI
- CPAN-Scalar-Util-Reftype
- CPAN-Parse-HTTP-UserAgent
- CPAN-Acme-CPANAuthors-Turkish
- CPAN-tools
- CPAN-Device-CableModem-Motorola-SB4200
- CPAN-Text-Template-Simple
- CPAN-CGI-Auth-Basic
- CPAN-GD-Thumbnail
- CPAN-Lingua-Any-Numbers
- CPAN-MP3-M3U-Parser
- CPAN-PHP-Session-DBI
- CPAN-Sys-Info
- CPAN-Sys-Info-Base
- CPAN-Sys-Info-Driver-BSD
- CPAN-Sys-Info-Driver-Linux
- CPAN-Sys-Info-Driver-Unknown
- CPAN-Test-Sys-Info
- Send message
6 months ago
Changed (Δ10.9 KB):
Build.PL (5 lines added, 0 lines removed)
Changes (3 lines added, 0 lines removed)
MANIFEST (1 lines added, 1 lines removed)
SPEC (7 lines added, 2 lines removed)
eg/demo.pl (82 lines added, 72 lines removed)
lib/GD/SecurityImage.pm (181 lines added, 155 lines removed)
lib/GD/SecurityImage/GD.pm (245 lines added, 165 lines removed)
lib/GD/SecurityImage/Magick.pm (43 lines added, 55 lines removed)
lib/GD/SecurityImage/Styles.pm (18 lines added, 27 lines removed)
t/03-info_text.t (3 lines added, 4 lines removed)
t/04-backend.t (25 lines added, 19 lines removed)
t/05-version.t (9 lines added, 8 lines removed)
t/06-version_magick.t (15 lines added, 12 lines removed)
t/98-gd.t (14 lines added, 5 lines removed)
t/99-magick.t (16 lines added, 7 lines removed)
t/lib/Test/GDSI.pm (209 lines added, 0 lines removed)
t/magick.pl (12 lines added, 8 lines removed)
t/t.api
1 |
1 |
use strict; |
2 |
use warnings; |
|
2 |
3 |
use lib qw( builder ); |
3 |
4 |
use Build; |
4 |
5 |
|
5 |
6 |
my $mb = Build->new; |
6 |
7 |
$mb->change_versions(1); |
8 |
$mb->copyright_first_year( '2004' ); |
|
9 |
$mb->add_pod_author_copyright_license(1); |
|
7 |
10 |
$mb->create_build_script; |
11 |
||
12 |
1; |
| … | … | @@ -2,6 +2,9 @@ Revision history for Perl extension GD:: |
2 |
2 |
|
3 |
3 |
Time zone is GMT+2. |
4 |
4 |
|
5 |
1.71 Fri Oct 2 05:50:46 2009 |
|
6 |
=> Major Perl::Critic refactoring. |
|
7 |
||
5 |
8 |
1.70 Thu Apr 30 16:56:32 2009 |
6 |
9 |
=> Made a mistake. version checking test is really gone now. |
7 |
10 |
| … | … | @@ -9,7 +9,7 @@ t/06-version_magick.t |
9 |
9 |
t/98-gd.t |
10 |
10 |
t/99-magick.t |
11 |
11 |
t/magick.pl |
12 |
t/ |
|
12 |
t/lib/Test/GDSI.pm |
|
13 |
13 |
eg/demo.pl |
14 |
14 |
Changes |
15 |
15 |
Build.PL |
| … | … | @@ -15,8 +15,13 @@ print qq~ |
15 |
15 |
|
16 |
16 |
{ |
17 |
17 |
module_name => 'GD::SecurityImage', |
18 |
requires => { 'GD' => 0 }, |
|
19 |
recommends => { 'Image::Magick' => '6.0.4'}, |
|
18 |
requires => { |
|
19 |
'GD' => 0, |
|
20 |
( $] < 5.006 ? ( 'warnings::compat' => 0 ) : ()), |
|
21 |
}, |
|
22 |
recommends => { |
|
23 |
'Image::Magick' => '6.0.4', |
|
24 |
}, |
|
20 |
25 |
meta_merge => { |
21 |
26 |
resources => { |
22 |
27 |
repository => 'http://bitbucket.org/burak/cpan-gd-securityimage/', |
2 |
2 |
# -> GD::SecurityImage demo program |
3 |
3 |
|
4 |
4 |
# See the document section after "__END__" for license and other information. |
5 |
package |
|
5 |
package Demo; |
|
6 |
6 |
use strict; |
7 |
use |
|
7 |
use warnings; |
|
8 |
use vars qw( $VERSION ); |
|
8 |
9 |
use CGI qw( header escapeHTML ); |
9 |
10 |
use Cwd; |
11 |
use Carp qw( croak ); |
|
12 |
use constant SALT_RANDOM => 100; |
|
13 |
use constant MAGICK_PTSIZE => 12; |
|
14 |
use constant GD_PTSIZE => 8; |
|
10 |
15 |
|
11 |
|
|
16 |
my %config = ( |
|
12 |
17 |
database => 'gdsi', # database name (for session storage) |
13 |
18 |
table_name => 'sessions', # only change this value, if you *really* have to use another table name. Also change the SQL code below. |
14 |
19 |
user => 'root', # database user name |
15 |
pass => '', # database user's password |
|
16 |
font => getcwd."/StayPuft.ttf", # ttf font. change this to an absolute path if getcwd is failing |
|
20 |
pass => q{}, # database user's password |
|
21 |
font => getcwd.'/StayPuft.ttf', # ttf font. change this to an absolute path if getcwd is failing |
|
17 |
22 |
itype => 'png', # image format. set this to gif or png or jpeg |
18 |
23 |
use_magick => 0, # use Image::Magick or GD |
19 |
24 |
img_stat => 1, # display statistics on the image? |
20 |
program => |
|
25 |
program => q{}, # if CGI.pm fails to locate program url, set this value. |
|
21 |
26 |
); |
22 |
27 |
|
23 |
28 |
# You'll need this to create the sessions table. |
| … | … | @@ -25,7 +30,7 @@ use Cwd; |
25 |
30 |
|
26 |
31 |
# - - - - - - - - - - - - - - > S T A R T P R O G R A M < - - - - - - - - - - - - - - # |
27 |
32 |
|
28 |
$VERSION = '1. |
|
33 |
$VERSION = '1.50'; |
|
29 |
34 |
|
30 |
35 |
use constant REQUIREDMODS => qw( |
31 |
36 |
DBI |
| … | … | @@ -44,46 +49,49 @@ BEGIN { |
44 |
49 |
local $SIG{__DIE__}; |
45 |
50 |
local $@; |
46 |
51 |
my $mod = shift; |
47 |
eval "require $mod"; |
|
48 |
push @errors, { module => $mod, error => $@ } if $@; |
|
52 |
my $eok = eval "require $mod; 1;"; |
|
53 |
push @errors, { module => $mod, error => $@ } if $@ || ! $eok; |
|
49 |
54 |
}; |
50 |
55 |
$test->($_) foreach REQUIREDMODS; |
51 |
56 |
if ( @errors ) { |
52 |
57 |
my $err = qq{<pre>This demo program needs several CPAN modules to run:\n\n}; |
53 |
58 |
foreach my $e ( @errors ) { |
54 |
$err .= q |
|
59 |
$err .= q~<b><span style="color:red">[FAILED]</span>~ |
|
55 |
60 |
. qq~ $e->{module}</b>: $e->{error}<br />~; |
56 |
61 |
} |
57 |
print header . $err . '</pre>' |
|
62 |
print header . $err . '</pre>' or croak "Can not print to STDOUT: $!"; |
|
58 |
63 |
exit; |
59 |
64 |
} |
60 |
$SIG{__DIE__} = sub { |
|
61 |
print header . qq~ |
|
62 |
<h1 style="color:red;font-weight:bold" |
|
63 |
>FATAL ERROR</h1> |
|
64 |
@_ |
|
65 |
~; |
|
66 |
exit; |
|
67 |
}; |
|
68 |
65 |
} |
69 |
66 |
|
70 |
my $NOT_EXISTS = quotemeta "Object does not exist in the data store"; |
|
67 |
local $SIG{__DIE__} = sub { |
|
68 |
print header . <<"ERROR" or croak "Can not print to STDOUT: $!"; |
|
69 |
<h1 style="color:red;font-weight:bold" |
|
70 |
>FATAL ERROR</h1> |
|
71 |
@_ |
|
72 |
ERROR |
|
73 |
exit; |
|
74 |
}; |
|
75 |
||
76 |
my $NOT_EXISTS = quotemeta 'Object does not exist in the data store'; |
|
71 |
77 |
|
72 |
78 |
run() if not caller; # if you require this, you'll need to call demo::run() |
73 |
79 |
|
74 |
80 |
sub TEST_FONT_EXISTENCE { |
75 |
81 |
if ( not $config{use_magick} ) { |
76 |
if ( $config{font} =~ m[\s]s ) { |
|
77 |
die "The font path '$config{font}' has a space in it. GD hates spaces!"; |
|
82 |
if ( $config{font} =~ m{\s}xms ) { |
|
83 |
croak "The font path '$config{font}' has a space in it. GD hates spaces!"; |
|
78 |
84 |
} |
79 |
85 |
} |
80 |
local *FONTFILE; |
|
81 |
if ( open FONTFILE, $config{font} ) { |
|
82 |
|
|
86 |
require IO::File; |
|
87 |
my $FONTFILE = IO::File->new; |
|
88 |
if ( $FONTFILE->open( $config{font} ) ) { |
|
89 |
$FONTFILE->close; |
|
83 |
90 |
} |
84 |
91 |
else { |
85 |
|
|
92 |
croak qq~I can not open/find the font file in '$config{font}': $!~; |
|
86 |
93 |
} |
94 |
return; |
|
87 |
95 |
} |
88 |
96 |
|
89 |
97 |
sub new { |
| … | … | @@ -92,11 +100,11 @@ sub new { |
92 |
100 |
my $self = { |
93 |
101 |
ISDISPLAY => 0, |
94 |
102 |
SID => undef, |
95 |
CPAN => |
|
103 |
CPAN => 'http://search.cpan.org/dist', |
|
96 |
104 |
IS_GD => 0, |
97 |
105 |
}; |
98 |
106 |
bless $self, $class; |
99 |
|
|
107 |
return $self; |
|
100 |
108 |
} |
101 |
109 |
|
102 |
110 |
sub run { |
| … | … | @@ -110,7 +118,7 @@ sub run { |
110 |
118 |
$self->{program} = $config{program}; |
111 |
119 |
if ( ! $self->{program} ){ |
112 |
120 |
# it is possible to get the url as "demo.pl??foo=bar" |
113 |
($self->{program}, my @jp) = split |
|
121 |
($self->{program}, my @jp) = split m{\?}xms, $self->{cgi}->url; |
|
114 |
122 |
} |
115 |
123 |
my %options = $self->all_options; |
116 |
124 |
my %styles = $self->all_styles; |
| … | … | @@ -132,14 +140,14 @@ sub run { |
132 |
140 |
my %session; |
133 |
141 |
my $create_ses = sub { # fetch/create session |
134 |
142 |
my $sid = @_ ? undef : $self->{cgi}->cookie('GDSI_ID'); |
135 |
tie %session, 'Apache::Session::MySQL', $sid, { |
|
143 |
tie %session, 'Apache::Session::MySQL', $sid, { ## no critic (Miscellanea::ProhibitTies) |
|
136 |
144 |
Handle => $dbh, |
137 |
145 |
LockHandle => $dbh, |
138 |
146 |
TableName => $config{table_name}, |
139 |
147 |
}; |
140 |
148 |
}; |
141 |
149 |
|
142 |
|
|
150 |
my $eok = eval { $create_ses->() 1; }; |
|
143 |
151 |
|
144 |
152 |
# I'm doing a little trick to by-pass exceptions if the session id |
145 |
153 |
# coming from the user no longer exists in the database. |
| … | … | @@ -151,13 +159,13 @@ sub run { |
151 |
159 |
$create_ses->('new'); |
152 |
160 |
} |
153 |
161 |
|
154 |
if ( |
|
162 |
if ( ! $session{security_code} ) { |
|
155 |
163 |
$session{security_code} = $self->_random; # initialize random code |
156 |
164 |
} |
157 |
165 |
|
158 |
166 |
$self->{ISDISPLAY} = $self->{cgi}->param('display') || 0; |
159 |
167 |
$self->{SID} = $session{_session_id}; |
160 |
my $output = |
|
168 |
my $output = q{}; # output buffer |
|
161 |
169 |
|
162 |
170 |
if ( $self->{ISDISPLAY} ) { |
163 |
171 |
$START = Time::HiRes::time(); |
| … | … | @@ -178,14 +186,14 @@ sub run { |
178 |
186 |
|
179 |
187 |
untie %session; |
180 |
188 |
$dbh->disconnect; |
181 |
print $output |
|
189 |
print $output or croak "Can not print to STDOUT: $!"; |
|
182 |
190 |
exit; |
183 |
191 |
} |
184 |
192 |
|
185 |
193 |
sub process { |
186 |
194 |
my $self = shift; |
187 |
my $ses = shift || die "security_code from session is missing"; |
|
188 |
my $code = $self->{cgi}->param('code') || ''; |
|
195 |
my $ses = shift || croak 'Security_code from session is missing'; |
|
196 |
my $code = $self->{cgi}->param('code') || q{}; |
|
189 |
197 |
my $pass = $self->iseq( $code, $ses ); |
190 |
198 |
my $meth = $pass ? '_congrats' : '_failure'; |
191 |
199 |
return $self->$meth( $code, $ses ); |
| … | … | @@ -193,35 +201,36 @@ sub process { |
193 |
201 |
|
194 |
202 |
sub backenduri { |
195 |
203 |
my $self = shift; |
196 |
my $rv = q |
|
204 |
my $rv = q{Security image generated with <b>}; |
|
197 |
205 |
$rv .= $self->{IS_GD} |
198 |
? qq~<a href="$self->{CPAN}/GD" target="_blank">GD</a> v$GD::VERSION~ |
|
206 |
? qq~<a href="$self->{CPAN}/GD" target="_blank">GD</a> v$GD::VERSION~ |
|
199 |
207 |
: qq~<a href="$self->{CPAN}/PerlMagick" target="_blank">Image::Magick</a> v$Image::Magick::VERSION~; |
200 |
208 |
return $rv . '</b>'; |
201 |
209 |
} |
202 |
210 |
|
203 |
sub _random { |
|
211 |
sub _random { return String::Random->new->randregex('\d\d\d\d\d\d') } |
|
204 |
212 |
|
205 |
213 |
sub _failure { |
206 |
214 |
my $self = shift; |
207 |
my $code = CGI::escapeHTML(shift || ''); |
|
208 |
my $ses = shift || ''; |
|
209 |
my $ |
|
215 |
my $code = CGI::escapeHTML(shift || q{}); |
|
216 |
my $ses = shift || q{}; |
|
217 |
my $rv = <<"FAIL"; |
|
210 |
218 |
<b>'${code}' != '${ses}'</b> |
211 |
219 |
<br /> |
212 |
220 |
<span style="color:red;font-weight:bold"> |
213 |
221 |
You have failed to identify yourself as a human! |
214 |
222 |
</span> |
215 |
<br /> |
|
223 |
<br /> |
|
224 |
FAIL |
|
216 |
225 |
$rv .= $self->form(); |
217 |
226 |
return $rv; |
218 |
227 |
} |
219 |
228 |
|
220 |
229 |
sub _congrats { |
221 |
230 |
my $self = shift; |
222 |
my $form = shift || ''; |
|
223 |
my $ses = shift || ''; |
|
224 |
|
|
231 |
my $form = shift || q{}; |
|
232 |
my $ses = shift || q{}; |
|
233 |
return <<"PASS"; |
|
225 |
234 |
<b>'$form' == '$ses'</b> |
226 |
235 |
<br /> |
227 |
236 |
<span style="color:#009700;font-weight:bold"> |
| … | … | @@ -230,20 +239,19 @@ sub _congrats { |
230 |
239 |
<br /> |
231 |
240 |
<br /> |
232 |
241 |
<a href="$self->{program}">Try again</a> |
233 |
~; |
|
242 |
PASS |
|
234 |
243 |
} |
235 |
244 |
|
236 |
245 |
sub iseq { |
237 |
246 |
my $self = shift; |
238 |
247 |
my $form = shift || return; |
239 |
248 |
my $ses = shift || return; |
240 |
return if $form =~ m{[^0-9]} |
|
249 |
return if $form =~ m{[^0-9]}xms; |
|
241 |
250 |
return $form eq $ses; |
242 |
251 |
} |
243 |
252 |
|
244 |
253 |
sub myheader { |
245 |
my $self = shift; |
|
246 |
my %o = @_; |
|
254 |
my($self, %o) = @_; |
|
247 |
255 |
my $display = $self->{ISDISPLAY}; |
248 |
256 |
my $type = $o{type} ? $o{type} |
249 |
257 |
: $display ? 'image/'.$config{itype} |
| … | … | @@ -253,7 +261,7 @@ sub myheader { |
253 |
261 |
-value => $self->{SID}, |
254 |
262 |
); |
255 |
263 |
return $self->{cgi}->header( |
256 |
-type => $type, |
|
264 |
-type => $type, |
|
257 |
265 |
-cookie => $c |
258 |
266 |
); |
259 |
267 |
} |
| … | … | @@ -262,7 +270,7 @@ sub myheader { |
262 |
270 |
|
263 |
271 |
sub help { |
264 |
272 |
my $self = shift; |
265 |
|
|
273 |
return <<"HELP"; |
|
266 |
274 |
|
267 |
275 |
If you want to change the image generation options, open this file with |
268 |
276 |
a text editor and search for the <b>%config</b> hash. |
| … | … | @@ -352,14 +360,14 @@ used for session data storage. |
352 |
360 |
|
353 |
361 |
</table> |
354 |
362 |
|
355 |
~; |
|
363 |
HELP |
|
356 |
364 |
} |
357 |
365 |
|
358 |
366 |
sub form { |
359 |
367 |
my $self = shift; |
360 |
368 |
# by-pass browser cache with this random fake value |
361 |
my $salt = '&salt=' . $$ . time . rand(100); |
|
362 |
return qq~ |
|
369 |
my $salt = '&salt=' . $$ . time . rand SALT_RANDOM; |
|
370 |
return <<"FORM"; |
|
363 |
371 |
<form action="$self->{program}" method="post"> |
364 |
372 |
<table border="0" cellpadding="2" cellspacing="1"> |
365 |
373 |
<tr> |
| … | … | @@ -377,12 +385,13 @@ sub form { |
377 |
385 |
</tr> |
378 |
386 |
</table> |
379 |
387 |
</form> |
380 |
~ |
|
388 |
FORM |
|
381 |
389 |
} |
382 |
390 |
|
383 |
391 |
sub html_head { |
384 |
392 |
my $self = shift; |
385 |
|
|
393 |
return <<"HTML_HEAD"; |
|
394 |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
|
386 |
395 |
"http://www.w3.org/TR/html4/loose.dtd"> |
387 |
396 |
<html> |
388 |
397 |
<head> |
| … | … | @@ -414,7 +423,7 @@ sub html_head { |
414 |
423 |
<h2><a href = "$self->{CPAN}/GD-SecurityImage" |
415 |
424 |
target = "_blank" |
416 |
425 |
>GD::SecurityImage</a> v$GD::SecurityImage::VERSION - DEMO v$VERSION</h2> |
417 |
~ |
|
426 |
HTML_HEAD |
|
418 |
427 |
} |
419 |
428 |
|
420 |
429 |
sub html_foot { |
| … | … | @@ -441,14 +450,14 @@ sub create_image { # create a security i |
441 |
450 |
lines => $s->{lines}, |
442 |
451 |
bgcolor => $s->{bgcolor}, |
443 |
452 |
%{ $self->{rnd_opt} }, |
444 |
) |
|
445 |
->random ($code) |
|
446 |
->create (ttf => $s->{name}, $s->{text_color}, $s->{line_color}) |
|
447 |
->particle($s->{dots} ? ($s->{particle}, $s->{dots}) |
|
448 |
: ($s->{particle}) |
|
449 |
453 |
); |
454 |
$i->random ($code) |
|
455 |
->create (ttf => $s->{name}, $s->{text_color}, $s->{line_color}) |
|
456 |
->particle($s->{dots} ? ($s->{particle}, $s->{dots}) |
|
457 |
: ($s->{particle}) |
|
458 |
); |
|
450 |
459 |
if ($i->gdbox_empty) { |
451 |
|
|
460 |
croak qq~An error occurred while opening the font file '$config{font}'. ~ |
|
452 |
461 |
.qq~Please set font option to an "exact" path, not relative. Error: $@~; |
453 |
462 |
} |
454 |
463 |
if ($config{img_stat}) { |
| … | … | @@ -457,13 +466,13 @@ sub create_image { # create a security i |
457 |
466 |
y => 'up', |
458 |
467 |
gd => 1, |
459 |
468 |
strip => 1, |
460 |
color => "#000000", |
|
461 |
scolor => "#FFFFFF", |
|
469 |
color => '#000000', |
|
470 |
scolor => '#FFFFFF', |
|
462 |
471 |
# low-level access to an object table is not a good thing, |
463 |
472 |
# since the author can change/delete it without notification |
464 |
473 |
# in later releases ;) |
465 |
ptsize => $i->{IS_MAGICK} ? 12 : 8, |
|
466 |
text => sprintf("Security Image generated at %.3f seconds", |
|
474 |
ptsize => $i->{IS_MAGICK} ? MAGICK_PTSIZE : GD_PTSIZE, |
|
475 |
text => sprintf('Security Image generated at %.3f seconds', |
|
467 |
476 |
Time::HiRes::time() - $START), |
468 |
477 |
); |
469 |
478 |
} |
| … | … | @@ -531,6 +540,7 @@ sub all_options { |
531 |
540 |
} |
532 |
541 |
|
533 |
542 |
sub all_styles { |
543 |
## no critic (ValuesAndExpressions::ProhibitMagicNumbers) |
|
534 |
544 |
return ec => { |
535 |
545 |
name => 'ec', |
536 |
546 |
lines => 16, |
| … | … | @@ -541,7 +551,7 @@ sub all_styles { |
541 |
551 |
}, |
542 |
552 |
ellipse => { |
543 |
553 |
name => 'ellipse', |
544 |
lines => 15, |
|
554 |
lines => 15, |
|
545 |
555 |
bgcolor => [208, 202, 206], |
546 |
556 |
text_color => [184, 20, 180], |
547 |
557 |
line_color => [184, 20, 180], |
| … | … | @@ -549,9 +559,9 @@ sub all_styles { |
549 |
559 |
}, |
550 |
560 |
circle => { |
551 |
561 |
name => 'circle', |
552 |
lines => 40, |
|
562 |
lines => 40, |
|
553 |
563 |
bgcolor => [210, 215, 196], |
554 |
text_color => [ 63, 143, 167], |
|
564 |
text_color => [ 63, 143, 167], |
|
555 |
565 |
line_color => [210, 215, 196], |
556 |
566 |
particle => 3500, |
557 |
567 |
}, |
| … | … | @@ -566,7 +576,7 @@ sub all_styles { |
566 |
576 |
rect => { |
567 |
577 |
name => 'rect', |
568 |
578 |
lines => 30, |
569 |
text_color => [ 63, 143, 167], |
|
579 |
text_color => [ 63, 143, 167], |
|
570 |
580 |
line_color => [226, 223, 169], |
571 |
581 |
particle => 2000, |
572 |
582 |
}, |
Up to file-list lib/GD/SecurityImage.pm:
1 |
1 |
package GD::SecurityImage; |
2 |
2 |
use strict; |
3 |
use |
|
3 |
use warnings; |
|
4 |
use vars qw[@ISA $VERSION $BACKEND]; |
|
4 |
5 |
use GD::SecurityImage::Styles; |
5 |
6 |
use Carp qw(croak); |
7 |
use constant RGB_WHITE => ( 255, 255, 255 ); |
|
8 |
use constant RGB_BLACK => ( 0, 0, 0 ); |
|
9 |
use constant RANDOM_DATA => ( 0..9 ); |
|
10 |
use constant FULL_CIRCLE => 360; |
|
11 |
use constant DEFAULT_ANGLES => (0,5,8,15,22,26,29,33,35,36,40,43,45,53,56); |
|
6 |
12 |
|
7 |
|
|
13 |
use constant DEFAULT_WIDTH => 80; |
|
14 |
use constant DEFAULT_HEIGHT => 30; |
|
15 |
use constant DEFAULT_PTSIZE => 20; |
|
16 |
use constant DEFAULT_LINES => 10; |
|
17 |
||
18 |
use constant MAX_RGB_VALUE => 255; |
|
19 |
use constant PARTICLE_MULTIPLIER => 20; |
|
20 |
use constant MAX_RGB_PARAMS => 3; |
|
21 |
||
22 |
$VERSION = '1.71'; |
|
8 |
23 |
|
9 |
24 |
sub import { |
10 |
my $class = shift; |
|
11 |
my %opt = scalar(@_) % 2 ? () : (@_); |
|
25 |
my($class, @args) = @_; |
|
26 |
my %opt = @args % 2 ? () : @args; |
|
12 |
27 |
# init/reset globals |
13 |
$BACKEND = ''; # name of the back-end |
|
14 |
@ISA = (); |
|
28 |
$BACKEND = q{}; # name of the back-end |
|
29 |
@ISA = (); ## no critic (ClassHierarchies::ProhibitExplicitISA) |
|
15 |
30 |
# load the drawing interface |
16 |
31 |
if ( exists $opt{use_magick} && $opt{use_magick} ) { |
17 |
32 |
require GD::SecurityImage::Magick; |
18 |
33 |
$BACKEND = 'Magick'; |
19 |
34 |
} |
20 |
35 |
elsif ( exists $opt{backend} && $opt{backend} ) { |
21 |
my $be = __PACKAGE__.'::'.$opt{backend}; |
|
22 |
eval "require $be"; |
|
36 |
my $be = __PACKAGE__.q{::}.$opt{backend}; |
|
37 |
my $eok = eval "require $be"; |
|
23 |
38 |
croak "Unable to locate the $class back-end $be: $@" if $@; |
24 |
39 |
$BACKEND = $opt{backend} eq 'AC' ? 'GD' : $opt{backend}; |
25 |
40 |
} |
| … | … | @@ -27,15 +42,15 @@ sub import { |
27 |
42 |
require GD::SecurityImage::GD; |
28 |
43 |
$BACKEND = 'GD'; |
29 |
44 |
} |
30 |
push @ISA, 'GD::SecurityImage::' . $BACKEND; |
|
31 |
push @ISA, qw(GD::SecurityImage::Styles); # load styles |
|
45 |
push @ISA, 'GD::SecurityImage::' . $BACKEND, ## no critic (ClassHierarchies::ProhibitExplicitISA) |
|
46 |
qw(GD::SecurityImage::Styles); # load styles |
|
32 |
47 |
return; |
33 |
48 |
} |
34 |
49 |
|
35 |
50 |
sub new { |
36 |
my |
|
51 |
my($class, @args) = @_; |
|
37 |
52 |
$BACKEND || croak "You didn't import $class!"; |
38 |
my %opt = |
|
53 |
my %opt = @args % 2 ? () : @args; |
|
39 |
54 |
|
40 |
55 |
my $self = { |
41 |
56 |
IS_MAGICK => $BACKEND eq 'Magick', |
| … | … | @@ -44,7 +59,7 @@ sub new { |
44 |
59 |
DISABLED => {}, # list of methods that a backend (or some older version of backend) can't do |
45 |
60 |
MAGICK => {}, # Image::Magick configuration options |
46 |
61 |
GDBOX_EMPTY => 0, # GD::SecurityImage::GD::insert_text() failed? |
47 |
_RANDOM_NUMBER_ => |
|
62 |
_RANDOM_NUMBER_ => q{}, # random security code |
|
48 |
63 |
_RNDMAX_ => 6, # maximum number of characters in a random string. |
49 |
64 |
_COLOR_ => {}, # text and line colors |
50 |
65 |
_CREATECALLED_ => 0, # create() called? (check for particle()) |
| … | … | @@ -52,16 +67,59 @@ sub new { |
52 |
67 |
}; |
53 |
68 |
bless $self, $class; |
54 |
69 |
|
70 |
my %options = $self->_new_options( %opt ); |
|
71 |
||
72 |
if ( $opt{text_location} |
|
73 |
&& ref $opt{text_location} |
|
74 |
&& ref $opt{text_location} eq 'HASH' ) { |
|
75 |
$self->{_TEXT_LOCATION_} = { %{$opt{text_location}}, _place_ => 1 }; |
|
76 |
} |
|
77 |
else { |
|
78 |
$self->{_TEXT_LOCATION_}{_place_} = 0; |
|
79 |
} |
|
80 |
||
81 |
$self->{_RNDMAX_} = $options{rndmax}; |
|
82 |
||
83 |
$self->{$_} = $options{$_} foreach keys %options; |
|
84 |
||
85 |
if ( $self->{angle} ) { # validate angle |
|
86 |
$self->{angle} = FULL_CIRCLE + $self->{angle} if $self->{angle} < 0; |
|
87 |
if ( $self->{angle} > FULL_CIRCLE ) { |
|
88 |
croak 'Angle parameter can take values in the range -360..360'; |
|
89 |
} |
|
90 |
} |
|
91 |
||
92 |
if ( $self->{scramble} ) { |
|
93 |
if ( $self->{angle} ) { |
|
94 |
# Does the user want a fixed angle? |
|
95 |
push @{ $self->{_ANGLES_} }, $self->{angle}; |
|
96 |
} |
|
97 |
else { |
|
98 |
# Generate angle range. The reason for hardcoding these is; |
|
99 |
# it'll be less random for 0..60 range |
|
100 |
push @{ $self->{_ANGLES_} }, DEFAULT_ANGLES; |
|
101 |
# push negatives |
|
102 |
push @{ $self->{_ANGLES_} }, |
|
103 |
map {FULL_CIRCLE - $_} @{ $self->{_ANGLES_} }; |
|
104 |
} |
|
105 |
} |
|
106 |
||
107 |
$self->init; |
|
108 |
return $self; |
|
109 |
} |
|
110 |
||
111 |
sub _new_options { |
|
112 |
my($self, %opt) = @_; |
|
55 |
113 |
my %options = ( |
56 |
width => $opt{width} || 80, |
|
57 |
height => $opt{height} || 30, |
|
58 |
ptsize => $opt{ptsize} || 20, |
|
59 |
lines => $opt{lines} || 10, |
|
114 |
width => $opt{width} || DEFAULT_WIDTH, |
|
115 |
height => $opt{height} || DEFAULT_HEIGHT, |
|
116 |
ptsize => $opt{ptsize} || DEFAULT_PTSIZE, |
|
117 |
lines => $opt{lines} || DEFAULT_LINES, |
|
60 |
118 |
rndmax => $opt{rndmax} || $self->{_RNDMAX_}, |
61 |
rnd_data => $opt{rnd_data} || [0..9], |
|
62 |
font => $opt{font} || '', |
|
63 |
gd_font => $self->gdf($opt{gd_font}) || '', |
|
64 |
bgcolor => $opt{bgcolor} || [255, 255, 255], |
|
119 |
rnd_data => $opt{rnd_data} || [ RANDOM_DATA ], |
|
120 |
font => $opt{font} || q{}, |
|
121 |
gd_font => $self->gdf($opt{gd_font}) || q{}, |
|
122 |
bgcolor => $opt{bgcolor} || [ RGB_WHITE ], |
|
65 |
123 |
send_ctobg => $opt{send_ctobg} || 0, |
66 |
124 |
frame => defined($opt{frame}) ? $opt{frame} : 1, |
67 |
125 |
scramble => $opt{scramble} || 0, |
| … | … | @@ -69,76 +127,46 @@ sub new { |
69 |
127 |
thickness => $opt{thickness} || 0, |
70 |
128 |
_ANGLES_ => [], # angle list for scrambled images |
71 |
129 |
); |
72 |
||
73 |
if($opt{text_location} && ref $opt{text_location} && ref $opt{text_location} eq 'HASH') { |
|
74 |
$self->{_TEXT_LOCATION_} = { %{$opt{text_location}}, _place_ => 1 }; |
|
75 |
} |
|
76 |
else { |
|
77 |
$self->{_TEXT_LOCATION_}{_place_} = 0; |
|
78 |
} |
|
79 |
$self->{_RNDMAX_} = $options{rndmax}; |
|
80 |
||
81 |
$self->{$_} = $options{$_} foreach keys %options; |
|
82 |
if($self->{angle}) { # validate angle |
|
83 |
$self->{angle} = 360 + $self->{angle} if $self->{angle} < 0; |
|
84 |
if($self->{angle} > 360) { |
|
85 |
croak "Angle parameter can take values in the range -360..360"; |
|
86 |
} |
|
87 |
} |
|
88 |
||
89 |
if ($self->{scramble}) { |
|
90 |
if ($self->{angle}) { |
|
91 |
# Does the user want a fixed angle? |
|
92 |
push @{ $self->{_ANGLES_} }, $self->{angle}; |
|
93 |
} |
|
94 |
else { |
|
95 |
# Generate angle range. The reason for hardcoding these is; |
|
96 |
# it'll be less random for 0..60 range |
|
97 |
push @{ $self->{_ANGLES_} }, (0,5,8,15,22,26,29,33,35,36,40,43,45,53,56); |
|
98 |
push @{ $self->{_ANGLES_} }, map {360 - $_} @{ $self->{_ANGLES_} }; # push negatives |
|
99 |
} |
|
100 |
} |
|
101 |
||
102 |
$self->init; |
|
103 |
return $self; |
|
130 |
return %options; |
|
104 |
131 |
} |
105 |
132 |
|
106 |
133 |
sub backends { |
107 |
134 |
my $self = shift; |
108 |
135 |
my $class = ref($self) || $self; |
109 |
136 |
my(@list, @dir_list); |
137 |
require Symbol; |
|
110 |
138 |
foreach my $inc (@INC) { |
111 |
139 |
my $dir = "$inc/GD/SecurityImage"; |
112 |
140 |
next unless -d $dir; |
113 |
local *DIR; |
|
114 |
opendir DIR, $dir or croak "opendir($dir) failed: $!"; |
|
115 |
my @dir = readdir DIR; |
|
116 |
closedir DIR; |
|
141 |
my $DIR = Symbol::gensym(); |
|
142 |
opendir $DIR, $dir or croak "opendir($dir) failed: $!"; |
|
143 |
my @dir = readdir $DIR; |
|
144 |
closedir $DIR; |
|
117 |
145 |
push @dir_list, $dir; |
118 |
146 |
foreach my $file (@dir) { |
119 |
147 |
next if -d $file; |
120 |
next if $file =~ m[^\.]; |
|
121 |
next if $file =~ m[^(Styles|AC|Handler)\.pm$]; |
|
122 |
|
|
148 |
next if $file =~ m{ \A [.] }xms; |
|
149 |
next if $file =~ m{ \A (Styles|AC|Handler)[.]pm \z}xms; |
|
150 |
$file =~ s{ [.]pm \z}{}xms; |
|
123 |
151 |
push @list, $file; |
124 |
152 |
} |
125 |
153 |
} |
126 |
if (defined wantarray) { |
|
127 |
return @list; |
|
128 |
} |
|
129 |
else { |
|
130 |
print "Available back-ends in $class v$VERSION are:\n\t" |
|
131 |
.join("\n\t", @list) |
|
132 |
."\n\n" |
|
133 |
."Search directories:\n\t" |
|
134 |
.join("\n\t", @dir_list); |
|
135 |
} |
|
154 |
||
155 |
return @list if defined wantarray; |
|
156 |
||
157 |
my $report = "Available back-ends in $class v$VERSION are:\n\t" |
|
158 |
. join("\n\t", @list) |
|
159 |
. "\n\n" |
|
160 |
. "Search directories:\n\t" |
|
161 |
. join "\n\t", @dir_list; |
|
162 |
print $report or croak "Unable to print to STDOUT: $!"; |
|
163 |
return; |
|
136 |
164 |
} |
137 |
165 |
|
138 |
166 |
sub gdf { |
139 |
my |
|
167 |
my($self, @args) = @_; |
|
140 |
168 |
return if not $self->{IS_GD}; |
141 |
return $self->gdfx( |
|
169 |
return $self->gdfx( @args ); |
|
142 |
170 |
} |
143 |
171 |
|
144 |
172 |
sub random_angle { |
| … | … | @@ -149,7 +177,7 @@ sub random_angle { |
149 |
177 |
return $r[int rand @r]; |
150 |
178 |
} |
151 |
179 |
|
152 |
sub random_str { |
|
180 |
sub random_str { return shift->{_RANDOM_NUMBER_} } |
|
153 |
181 |
|
154 |
182 |
sub random { |
155 |
183 |
my $self = shift; |
| … | … | @@ -164,24 +192,24 @@ sub random { |
164 |
192 |
$random .= $keys[int rand $lk] for 1..$self->{rndmax}; |
165 |
193 |
$self->{_RANDOM_NUMBER_} = $random; |
166 |
194 |
} |
167 |
return |
|
195 |
return defined wantarray ? $self : undef; |
|
168 |
196 |
} |
169 |
197 |
|
170 |
198 |
sub cconvert { # convert color codes |
171 |
199 |
# GD : return color index number |
172 |
200 |
# Image::Magick: return hex color code |
173 |
201 |
my $self = shift; |
174 |
my $data = shift || croak |
|
202 |
my $data = shift || croak 'Empty parameter passed to cconvert'; |
|
175 |
203 |
return $self->backend_cconvert($data) if not $self->{IS_CORE}; |
176 |
204 |
|
177 |
205 |
my $is_hex = $self->is_hex($data); |
178 |
206 |
my $magick_ok = $self->{IS_MAGICK} && $data && $is_hex; |
179 |
207 |
# data is a hex color code and Image::Magick has hex support |
180 |
208 |
return $data if $magick_ok; |
181 |
my $color_code = $data && |
|
182 |
! $is_hex && |
|
183 |
! ref($data) && |
|
184 |
$data !~ m{[^0-9]} && |
|
209 |
my $color_code = $data && |
|
210 |
! $is_hex && |
|
211 |
! ref($data) && |
|
212 |
$data !~ m{[^0-9]}xms && |
|
185 |
213 |
$data >= 0; |
186 |
214 |
|
187 |
215 |
if( $color_code ) { |
| … | … | @@ -195,15 +223,20 @@ sub cconvert { # convert color codes |
195 |
223 |
} |
196 |
224 |
|
197 |
225 |
my @rgb = $self->h2r($data); |
198 |
return |
|
226 |
return @rgb && $self->{IS_MAGICK} |
|
227 |
? $data |
|
228 |
: $self->_cconvert_new( $data, @rgb ); |
|
229 |
} |
|
199 |
230 |
|
231 |
sub _cconvert_new { |
|
232 |
my($self, $data, @rgb) = @_; |
|
200 |
233 |
$data = [@rgb] if @rgb; |
201 |
234 |
# initialize if not valid |
202 |
if( |
|
235 |
if(! $data || ! ref $data || ref $data ne 'ARRAY' || $#{$data} != 2) { |
|
203 |
236 |
$data = [0, 0, 0]; |
204 |
237 |
} |
205 |
238 |
foreach my $i (0..$#{$data}) { # check for bad values |
206 |
if ( |
|
239 |
if ( $data->[$i] > MAX_RGB_VALUE || $data->[$i] < 0 ) { |
|
207 |
240 |
$data->[$i] = 0; |
208 |
241 |
} |
209 |
242 |
} |
| … | … | @@ -227,10 +260,10 @@ sub create { |
227 |
260 |
|
228 |
261 |
# be a smart module and auto-disable ttf if we are under a prehistoric GD |
229 |
262 |
if ( not $self->{IS_MAGICK} ) { |
230 |
$method = 'normal' if $self->_versionlt( |
|
263 |
$method = 'normal' if $self->_versionlt( '1.20' ); |
|
231 |
264 |
} |
232 |
265 |
|
233 |
if |
|
266 |
if ( $method eq 'normal' && ! $self->{gd_font} ) { |
|
234 |
267 |
$self->{gd_font} = $self->gdf('giant'); |
235 |
268 |
} |
236 |
269 |
|
| … | … | @@ -248,37 +281,36 @@ sub create { |
248 |
281 |
} |
249 |
282 |
|
250 |
283 |
$self->{_CREATECALLED_}++; |
251 |
return |
|
284 |
return defined wantarray ? $self : undef; |
|
252 |
285 |
} |
253 |
286 |
|
254 |
287 |
sub particle { |
255 |
288 |
# Create random dots. They'll cover all over the surface |
256 |
289 |
my $self = shift; |
257 |
croak |
|
290 |
croak q{particle() must be called 'after' create()} if !$self->{_CREATECALLED_}; |
|
258 |
291 |
my $big = $self->{height} > $self->{width} ? $self->{height} : $self->{width}; |
259 |
my $f = shift || $big * |
|
292 |
my $f = shift || $big * PARTICLE_MULTIPLIER; # particle density |
|
260 |
293 |
my $dots = shift || 1; # number of multiple dots |
261 |
my $int = int $big / |
|
294 |
my $int = int $big / PARTICLE_MULTIPLIER; |
|
262 |
295 |
|
263 |
296 |
if ( ! $int ) { # RT#33629 |
264 |
warn "particle(): image dimension is so small to add particles |
|
297 |
warn "particle(): image dimension is so small to add particles\n"; |
|
265 |
298 |
return; |
266 |
299 |
} |
267 |
300 |
|
268 |
301 |
my @random; |
269 |
for (my $x = $int; $x <= $big; $x += $int) { |
|
302 |
for (my $x = $int; $x <= $big; $x += $int) { ## no critic (ControlStructures::ProhibitCStyleForLoops) |
|
270 |
303 |
push @random, $x; |
271 |
304 |
} |
272 |
305 |
|
273 |
306 |
my $tc = $self->{_COLOR_}{text}; |
274 |
307 |
my $len = @random; |
275 |
308 |
my $r = sub { $random[ int rand $len ] }; |
276 |
my($x, $y, $z); |
|
277 |
309 |
|
278 |
for (1..$f) { |
|
279 |
$x = int rand $self->{width}; |
|
280 |
$y = int rand $self->{height}; |
|
281 |
foreach $z (1..$dots) { |
|
310 |
for ( 1..$f ) { |
|
311 |
my $x = int rand $self->{width}; |
|
312 |
my $y = int rand $self->{height}; |
|
313 |
foreach my $z (1..$dots) { |
|
282 |
314 |
$self->setPixel($x + $z , $y + $z , $tc); |
283 |
315 |
$self->setPixel($x + $z + $r->(), $y + $z + $r->(), $tc); |
284 |
316 |
} |
| … | … | @@ -286,10 +318,10 @@ sub particle { |
286 |
318 |
undef @random; |
287 |
319 |
undef $r; |
288 |
320 |
|
289 |
return |
|
321 |
return defined wantarray ? $self : undef; |
|
290 |
322 |
} |
291 |
323 |
|
292 |
sub raw { |
|
324 |
sub raw { return shift->{image} } # raw image object |
|
293 |
325 |
|
294 |
326 |
sub info_text { # set text location |
295 |
327 |
# x => 'left|right', # text-X |
| … | … | @@ -300,34 +332,49 @@ sub info_text { # set text location |
300 |
332 |
# color => '#000000', # text color |
301 |
333 |
# scolor => '#FFFFFF', # strip color |
302 |
334 |
# text => 'blah', # modifies random code |
303 |
my $self = shift; |
|
304 |
croak "info_text() must be called 'after' create()" if not $self->{_CREATECALLED_}; |
|
305 |
my |
|
335 |
my($self, @args) = @_; |
|
336 |
croak q{info_text() must be called 'after' create()} if ! $self->{_CREATECALLED_}; |
|
337 |
my %o = @args % 2 ? () : ( qw/ x right y up strip 1 /, @args ); |
|
306 |
338 |
return if not %o; |
307 |
339 |
|
308 |
340 |
$self->{_TEXT_LOCATION_}{_place_} = 1; |
309 |
341 |
$o{scolor} = $self->cconvert($o{scolor}) if $o{scolor}; |
310 |
local $self->{_RANDOM_NUMBER_} = delete $o{text} if $o{text}; |
|
311 |
local $self->{_COLOR_}{text} = $self->cconvert(delete $o{color}) if $o{color}; |
|
312 |
local $self->{ptsize} = delete $o{ptsize} if $o{ptsize}; |
|
313 |
342 |
|
314 |
local $self->{scramble} = 0; # disable. we need a straight text |
|
315 |
local $self->{angle} = 0; # disable. RT:14618 |
|
343 |
my %restore = ( |
|
344 |
random => $self->{_RANDOM_NUMBER_}, |
|
345 |
color => $self->{_COLOR_}{text}, |
|
346 |
ptsize => $self->{ptsize}, |
|
347 |
scramble => $self->{scramble}, |
|
348 |
angle => $self->{angle}, |
|
349 |
); |
|
350 |
||
351 |
$self->{_RANDOM_NUMBER_} = delete $o{text} if $o{text}; |
|
352 |
$self->{_COLOR_}{text} = $self->cconvert(delete $o{color}) if $o{color}; |
|
353 |
$self->{ptsize} = delete $o{ptsize} if $o{ptsize}; |
|
354 |
$self->{scramble} = 0; # disable. we need a straight text |
|
355 |
$self->{angle} = 0; # disable. RT:14618 |
|
316 |
356 |
|
317 |
357 |
$self->{_TEXT_LOCATION_}->{$_} = $o{$_} foreach keys %o; |
318 |
358 |
$self->insert_text('ttf'); |
319 |
$self; |
|
359 |
||
360 |
# restore |
|
361 |
$self->{_RANDOM_NUMBER_} = $restore{random}; |
|
362 |
$self->{_COLOR_}{text} = $restore{color}; |
|
363 |
$self->{ptsize} = $restore{ptsize}; |
|
364 |
$self->{scramble} = $restore{scramble}; |
|
365 |
$self->{angle} = $restore{angle}; |
|
366 |
||
367 |
return $self; |
|
320 |
368 |
} |
321 |
369 |
|
322 |
370 |
#--------------------[ PRIVATE ]--------------------# |
323 |
371 |
|
324 |
372 |
sub add_strip { # adds a strip to the background of the text |
325 |
my $self = shift; |
|
326 |
my($x, $y, $box_w, $box_h) = @_; |
|
373 |
my($self, $x, $y, $box_w, $box_h) = @_; |
|
327 |
374 |
my $tl = $self->{_TEXT_LOCATION_}; |
328 |
375 |
my $c = $self->{_COLOR_} || {}; |
329 |
my $black = $self->cconvert( $c->{text} ? $c->{text} : [ 0, 0, 0 ] ); |
|
330 |
my $white = $self->cconvert( $tl->{scolor} ? $tl->{scolor} : [ 255, 255, 255 ] ); |
|
376 |
my $black = $self->cconvert( $c->{text} ? $c->{text} : [ RGB_BLACK ] ); |
|
377 |
my $white = $self->cconvert( $tl->{scolor} ? $tl->{scolor} : [ RGB_WHITE ] ); |
|
331 |
378 |
my $x2 = $tl->{x} eq 'left' ? $box_w : $self->{width}; |
332 |
379 |
my $y2 = $self->{height} - $box_h; |
333 |
380 |
my $i = $self->{IS_MAGICK} ? $self : $self->{image}; |
| … | … | @@ -340,11 +387,11 @@ sub add_strip { # adds a strip to the ba |
340 |
387 |
|
341 |
388 |
sub r2h { |
342 |
389 |
# Convert RGB to Hex |
343 |
my $self = shift; |
|
344 |
@_ == 3 || return; |
|
345 |
my $color = '#'; |
|
346 |
$color .= sprintf("%02x", $_) foreach @_; |
|
347 |
|
|
390 |
my($self, @args) = @_; |
|
391 |
return if @args != MAX_RGB_PARAMS; |
|
392 |
my $color = q{#}; |
|
393 |
$color .= sprintf '%02x', $_ foreach @args; |
|
394 |
return $color; |
|
348 |
395 |
} |
349 |
396 |
|
350 |
397 |
sub h2r { |
| … | … | @@ -352,27 +399,16 @@ sub h2r { |
352 |
399 |
my $self = shift; |
353 |
400 |
my $color = shift; |
354 |
401 |
return if ref $color; |
355 |
my @rgb = $color =~ m |
|
402 |
my @rgb = $color =~ m/\A \#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2}) \z/xmsi; |
|
356 |
403 |
return @rgb ? map { hex $_ } @rgb : undef; |
357 |
404 |
} |
358 |
405 |
|
359 |
406 |
sub is_hex { |
360 |
407 |
my $self = shift; |
361 |
408 |
my $data = shift; |
362 |
return $data =~ m |
|
409 |
return $data =~ m/ \A \#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2}) \z /xmsi; |
|
363 |
410 |
} |
364 |
411 |
|
365 |
sub AUTOLOAD { |
|
366 |
my $self = shift; |
|
367 |
my $class = ref $self; |
|
368 |
(my $name = $AUTOLOAD) =~ s,.*:,,; |
|
369 |
# fake method for GD compatibility. only GD has this |
|
370 |
return 0 if $name eq 'gdbox_empty'; |
|
371 |
croak "Unknown $class method '$name'"; |
|
372 |
} |
|
373 |
||
374 |
sub DESTROY {} |
|
375 |
||
376 |
412 |
1; |
377 |
413 |
|
378 |
414 |
__END__ |
| … | … | @@ -386,24 +422,28 @@ GD::SecurityImage - Security image (capt |
386 |
422 |
use GD::SecurityImage; |
387 |
423 |
|
388 |
424 |
# Create a normal image |
389 |
my $image = GD::SecurityImage->new(width => 80, |
|
390 |
height => 30, |
|
391 |
lines => 10, |
|
392 |
gd_font => 'giant'); |
|
393 |
$image->random($your_random_str); |
|
394 |
$image->create(normal => 'rect'); |
|
425 |
my $image = GD::SecurityImage->new( |
|
426 |
width => 80, |
|
427 |
height => 30, |
|
428 |
lines => 10, |
|
429 |
gd_font => 'giant', |
|
430 |
); |
|
431 |
$image->random( $your_random_str ); |
|
432 |
$image->create( normal => 'rect' ); |
|
395 |
433 |
my($image_data, $mime_type, $random_number) = $image->out; |
396 |
434 |
|
397 |
435 |
or |
398 |
436 |
|
399 |
437 |
# use external ttf font |
400 |
my $image = GD::SecurityImage->new(width => 100, |
|
401 |
height => 40, |
|
402 |
lines => 10, |
|
403 |
font => "/absolute/path/to/your.ttf", |
|
404 |
scramble => 1); |
|
405 |
$image->random($your_random_str); |
|
406 |
|
|
438 |
my $image = GD::SecurityImage->new( |
|
439 |
width => 100, |
|
440 |
height => 40, |
|
441 |
lines => 10, |
|
442 |
font => "/absolute/path/to/your.ttf", |
|
443 |
scramble => 1, |
|
444 |
); |
|
445 |
$image->random( $your_random_str ); |
|
446 |
$image->create( ttf => 'default' ); |
|
407 |
447 |
$image->particle; |
408 |
448 |
my($image_data, $mime_type, $random_number) = $image->out; |
409 |
449 |
|
| … | … | @@ -1371,18 +1411,4 @@ C<GD::SecurityImage>, you can comment/ra |
1371 |
1411 |
the C<CPAN Ratings> system: |
1372 |
1412 |
L<http://cpanratings.perl.org/dist/GD-SecurityImage>. |
1373 |
1413 |
|
1374 |
=head1 AUTHOR |
|
1375 |
||
1376 |
Burak GE<252>rsoy, E<lt>burakE<64>cpan.orgE<gt> |
|
1377 |
||
1378 |
=head1 COPYRIGHT |
|
1379 |
||
1380 |
Copyright 2004-2008 Burak GE<252>rsoy. All rights reserved. |
|
1381 |
||
1382 |
=head1 LICENSE |
|
1383 |
||
1384 |
This library is free software; you can redistribute it and/or modify |
|
1385 |
it under the same terms as Perl itself, either Perl version 5.8.8 or, |
|
1386 |
at your option, any later version of Perl 5 you may have available. |
|
1387 |
||
1388 |
1414 |
=cut |
Up to file-list lib/GD/SecurityImage/GD.pm:
1 |
1 |
package GD::SecurityImage::GD; |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw( $VERSION ); |
4 |
5 |
|
5 |
6 |
use constant LOWLEFTX => 0; # Lower left corner x |
| … | … | @@ -23,12 +24,21 @@ use constant NEWSTUFF => qw( ellipse |
23 |
24 |
use constant FORMATS => qw( png gif jpeg ); |
24 |
25 |
use constant GDFONTS => qw( Small Large MediumBold Tiny Giant ); |
25 |
26 |
|
27 |
use constant RGB_WHITE => (255, 255, 255); |
|
28 |
use constant BOX_SIZE => 7; |
|
29 |
||
30 |
use constant ROTATE_NONE => 0; |
|
31 |
use constant ROTATE_COUNTERCLOCKWISE => 90; |
|
32 |
use constant ROTATE_UPSIDEDOWN => 180; |
|
33 |
use constant ROTATE_CLOCKWISE => 270; |
|
34 |
use constant FULL_CIRCLE => 360; |
|
35 |
||
26 |
36 |
use GD; |
27 |
37 |
|
28 |
$VERSION = '1. |
|
38 |
$VERSION = '1.71'; |
|
29 |
39 |
|
30 |
40 |
# define the tff drawing method. |
31 |
my $TTF = __PACKAGE__->_versiongt( |
|
41 |
my $TTF = __PACKAGE__->_versiongt( '1.31' ) ? 'stringFT' : 'stringTTF'; |
|
32 |
42 |
|
33 |
43 |
sub init { |
34 |
44 |
# Create the image object |
| … | … | @@ -36,17 +46,18 @@ sub init { |
36 |
46 |
$self->{image} = GD::Image->new($self->{width}, $self->{height}); |
37 |
47 |
$self->cconvert($self->{bgcolor}); # set background color |
38 |
48 |
$self->setThickness($self->{thickness}) if $self->{thickness}; |
39 |
if ( $self->_versionlt( |
|
49 |
if ( $self->_versionlt( '2.07' ) ) { |
|
40 |
50 |
foreach my $prop ( NEWSTUFF ) { |
41 |
51 |
$self->{DISABLED}{$prop} = 1; |
42 |
52 |
} |
43 |
53 |
} |
54 |
return; |
|
44 |
55 |
} |
45 |
56 |
|
46 |
57 |
sub out { |
47 |
58 |
# return $image_data, $image_mime_type, $random_number |
48 |
my $self = shift; |
|
49 |
my %opt = scalar @_ % 2 ? () : (@_); |
|
59 |
my($self, @args) = @_; |
|
60 |
my %opt = @args % 2 ? () : @args; |
|
50 |
61 |
my $i = $self->{image}; |
51 |
62 |
my $type; |
52 |
63 |
if ( $opt{force} && $i->can($opt{force}) ){ |
| … | … | @@ -61,15 +72,16 @@ sub out { |
61 |
72 |
} |
62 |
73 |
} |
63 |
74 |
} |
64 |
my @args = (); |
|
75 |
||
76 |
my @iargs = (); |
|
65 |
77 |
if ( $opt{'compress'} ) { |
66 |
push @args, MAXCOMPRESS if $type eq 'png' and not $self->{DISABLED}{_png_compression}; |
|
67 |
push @args, $opt{'compress'} if $type eq 'jpeg'; |
|
78 |
push @iargs, MAXCOMPRESS if $type eq 'png' and not $self->{DISABLED}{_png_compression}; |
|
79 |
push @iargs, $opt{'compress'} if $type eq 'jpeg'; |
|
68 |
80 |
} |
69 |
return $i->$type(@ |
|
81 |
return $i->$type(@iargs), $type, $self->{_RANDOM_NUMBER_}; |
|
70 |
82 |
} |
71 |
83 |
|
72 |
sub gdbox_empty { |
|
84 |
sub gdbox_empty { return shift->{GDBOX_EMPTY} } |
|
73 |
85 |
|
74 |
86 |
sub gdfx { |
75 |
87 |
# Sets the font for simple GD usage. |
| … | … | @@ -85,37 +97,20 @@ sub gdfx { |
85 |
97 |
} |
86 |
98 |
} |
87 |
99 |
|
88 |
sub insert_text { |
|
89 |
# Draw text using GD |
|
90 |
my $self = shift; |
|
91 |
my $method = shift; |
|
92 |
my $key = $self->{_RANDOM_NUMBER_}; # random string |
|
93 |
my $ctext = $self->{_COLOR_}{text}; |
|
94 |
if ($method eq 'ttf') { |
|
95 |
require Math::Trig; |
|
96 |
# don' t draw. we just need info... |
|
97 |
my $info = sub { |
|
98 |
my $txt = shift; |
|
99 |
my $ang = shift || 0; |
|
100 |
$ang = Math::Trig::deg2rad($ang) if $ang; |
|
101 |
my @box = GD::Image->$TTF( $ctext, $self->{font}, $self->{ptsize}, $ang, 0, 0, $txt ); |
|
102 |
if ( not @box ) { # use fake values instead of die-ing |
|
103 |
$self->{GDBOX_EMPTY} = 1; # set this for error checking. |
|
104 |
$#box = 7; |
|
105 |
# lets initialize to silence the warnings |
|
106 |
$box[$_] = 1 for 0..$#box; |
|
107 |
} |
|
108 |
return @box; |
|
109 |
}; |
|
110 |
|
|
100 |
sub _insert_text_ttf_scramble { |
|
101 |
my($self, $key, $ctext) = @_; |
|
102 |
require Math::Trig; |
|
103 |
||
111 |
104 |
my @char; |
112 |
105 |
my $anglex; |
113 |
106 |
my $total = 0; |
114 |
my $space = [ |
|
107 |
my $space = [ $self->ttf_info( 0, 'A' ), 0, q{ } ]; |
|
115 |
108 |
my @randomy; |
116 |
109 |
my $sy = $space->[CHY] || 1; |
117 |
push(@randomy, $_, - $_) foreach $sy*1.2,$sy, $sy/2, $sy/4, $sy/8; |
|
118 |
foreach (split //, $key) { # get char parameters |
|
110 |
## no critic (ValuesAndExpressions::ProhibitMagicNumbers) |
|
111 |
push @randomy, $_, - $_ foreach $sy*1.2,$sy, $sy/2, $sy/4, $sy/8; |
|
112 |
## use critic |
|
113 |
foreach (split m{}xms, $key) { # get char parameters |
|
119 |
114 |
$anglex = $self->random_angle; |
120 |
115 |
$total += $space->[CHX]; |
121 |
116 |
push @char, [$self->ttf_info($anglex, $_), $anglex, $_], $space, $space, $space; |
| … | … | @@ -130,87 +125,132 @@ sub insert_text { |
130 |
125 |
$self->{image}->$TTF(@config, Math::Trig::deg2rad($box->[CHAR]), $x, $y, $box->[ANGLE]); |
131 |
126 |
$total -= $space->[CHX]; |
132 |
127 |
} |
128 |
return; |
|
129 |
} |
|
130 |
||
131 |
sub _insert_text_ttf_normal { |
|
132 |
my($self, $key, $ctext) = @_; |
|
133 |
require Math::Trig; |
|
134 |
# don' t draw. we just need info... |
|
135 |
my $info = sub { |
|
136 |
my $txt = shift; |
|
137 |
my $ang = shift || 0; |
|
138 |
$ang = Math::Trig::deg2rad($ang) if $ang; |
|
139 |
my @box = GD::Image->$TTF( |
|
140 |
$ctext, $self->{font}, $self->{ptsize}, $ang, 0, 0, $txt |
|
141 |
); |
|
142 |
if ( not @box ) { # use fake values instead of die-ing |
|
143 |
$self->{GDBOX_EMPTY} = 1; # set this for error checking. |
|
144 |
$#box = BOX_SIZE; |
|
145 |
# lets initialize to silence the warnings |
|
146 |
$box[$_] = 1 for 0..$#box; |
|
147 |
} |
|
148 |
return @box; |
|
149 |
}; |
|
150 |
||
151 |
my(@box, $x, $y); |
|
152 |
my $tl = $self->{_TEXT_LOCATION_}; |
|
153 |
if ( $tl->{_place_} ) { |
|
154 |
# put the text to one of the four corners in the image |
|
155 |
my $white = $self->cconvert( [ RGB_WHITE ] ); |
|
156 |
my $black = $self->cconvert($ctext); |
|
157 |
if ( $tl->{gd} ) { # draw with standard gd fonts |
|
158 |
$self->place_gd($key, $tl->{x}, $tl->{y}); |
|
159 |
return; # by-pass ttf method call... |
|
133 |
160 |
} |
134 |
161 |
else { |
135 |
my(@box,$x,$y); |
|
136 |
my $tl = $self->{_TEXT_LOCATION_}; |
|
137 |
if ($tl->{_place_}) { |
|
138 |
# put the text to one of the four corners in the image |
|
139 |
my $white = $self->cconvert([255,255,255]); |
|
140 |
my $black = $self->cconvert($ctext); |
|
141 |
if ( $tl->{gd} ) { # draw with standard gd fonts |
|
142 |
$self->place_gd($key, $tl->{x}, $tl->{y}); |
|
143 |
return; # by-pass ttf method call... |
|
144 |
} |
|
145 |
else { |
|
146 |
@box = $info->($key); |
|
147 |
$x = $tl->{x} eq 'left'? 0 : ($self->{width} - ($box[LOWRIGHTX] - $box[LOWLEFTX])); |
|
148 |
$y = $tl->{y} eq 'up' ? ($box[LOWLEFTY] - $box[UPLEFTY]) : $self->{height}-2; |
|
149 |
if ($tl->{strip}) { |
|
150 |
$self->add_strip($x, $y, $box[LOWRIGHTX] - $box[LOWLEFTX], $box[LOWLEFTY] - $box[UPLEFTY]); |
|
151 |
} |
|
152 |
} |
|
162 |
@box = $info->($key); |
|
163 |
$x = $tl->{x} eq 'left' |
|
164 |
? 0 |
|
165 |
: ( $self->{width} - ($box[LOWRIGHTX] - $box[LOWLEFTX]) ) |
|
166 |
; |
|
167 |
$y = $tl->{y} eq 'up' |
|
168 |
? ( $box[LOWLEFTY] - $box[UPLEFTY] ) |
|
169 |
: $self->{height} - 2 |
|
170 |
; |
|
171 |
if ($tl->{strip}) { |
|
172 |
$self->add_strip( |
|
173 |
$x, $y, $box[LOWRIGHTX] - $box[LOWLEFTX], $box[LOWLEFTY] - $box[UPLEFTY] |
|
174 |
); |
|
153 |
175 |
} |
154 |
else { |
|
155 |
@box = $info->($key); |
|
156 |
$x = ($self->{width} - ($box[LOWRIGHTX] - $box[LOWLEFTX])) / 2; |
|
157 |
$y = ($self->{height} - ($box[UPLEFTY] - $box[LOWLEFTY])) / 2; |
|
158 |
} |
|
159 |
# this needs a fix. adjust x,y |
|
160 |
if ($self->{angle}) { |
|
161 |
require Math::Trig; |
|
162 |
$self->{angle} = Math::Trig::deg2rad($self->{angle}); |
|
163 |
} |
|
164 |
else { |
|
165 |
$self->{angle} = 0; |
|
166 |
} |
|
167 |
$self->{image}->$TTF($ctext, $self->{font}, $self->{ptsize}, $self->{angle}, $x, $y, $key); |
|
168 |
176 |
} |
169 |
177 |
} |
170 |
178 |
else { |
171 |
if ($self->{scramble}) { |
|
172 |
# without ttf, we can only have 0 and 90 degrees. |
|
173 |
my @char; |
|
174 |
my @styles = qw(string stringUp); |
|
175 |
my $style = $styles[int rand @styles]; |
|
176 |
foreach (split //, $key) { # get char parameters |
|
177 |
push @char, [$_, $style], [' ','string']; |
|
178 |
$style = $style eq 'string' ? 'stringUp' : 'string'; |
|
179 |
} |
|
180 |
my $sw = $self->{gd_font}->width; |
|
181 |
my $sh = $self->{gd_font}->height; |
|
182 |
my($x, $y, $m); |
|
183 |
my $total = $sw * @char; |
|
184 |
foreach my $c (@char) { |
|
185 |
$m = $c->[1]; |
|
186 |
$x = ($self->{width} - $total) / 2; |
|
187 |
$y = $self->{height}/2 + ($m eq 'string' ? -$sh : $sh/2) / 2; |
|
188 |
$total -= $sw * 2; |
|
189 |
$self->{image}->$m($self->{gd_font}, $x, $y, $c->[0], $ctext); |
|
190 |
} |
|
191 |
} |
|
192 |
else { |
|
193 |
my $sw = $self->{gd_font}->width * length($key); |
|
194 |
my $sh = $self->{gd_font}->height; |
|
195 |
my $x = ($self->{width} - $sw) / 2; |
|
196 |
my $y = ($self->{height} - $sh) / 2; |
|
197 |
$self->{image}->string($self->{gd_font}, $x, $y, $key, $ctext); |
|
198 |
} |
|
179 |
@box = $info->($key); |
|
180 |
$x = ($self->{width} - ($box[LOWRIGHTX] - $box[LOWLEFTX])) / 2; |
|
181 |
$y = ($self->{height} - ($box[UPLEFTY] - $box[LOWLEFTY])) / 2; |
|
199 |
182 |
} |
183 |
||
184 |
# this needs a fix. adjust x,y |
|
185 |
$self->{angle} = $self->{angle} ? Math::Trig::deg2rad($self->{angle}) : 0; |
|
186 |
$self->{image}->$TTF( $ctext, $self->{font}, $self->{ptsize}, $self->{angle}, $x, $y, $key ); |
|
187 |
return; |
|
188 |
} |
|
189 |
||
190 |
sub _insert_text_gd_scramble { |
|
191 |
my($self, $key, $ctext) = @_; |
|
192 |
# without ttf, we can only have 0 and 90 degrees. |
|
193 |
my @char; |
|
194 |
my @styles = qw(string stringUp); |
|
195 |
my $style = $styles[int rand @styles]; |
|
196 |
foreach (split m{}xms, $key) { # get char parameters |
|
197 |
push @char, [ $_, $style ], [ q{ }, 'string' ]; |
|
198 |
$style = $style eq 'string' ? 'stringUp' : 'string'; |
|
199 |
} |
|
200 |
my $sw = $self->{gd_font}->width; |
|
201 |
my $sh = $self->{gd_font}->height; |
|
202 |
my($x, $y, $m); |
|
203 |
my $total = $sw * @char; |
|
204 |
foreach my $c (@char) { |
|
205 |
$m = $c->[1]; |
|
206 |
$x = ($self->{width} - $total) / 2; |
|
207 |
$y = $self->{height}/2 + ($m eq 'string' ? -$sh : $sh/2) / 2; |
|
208 |
$total -= $sw * 2; |
|
209 |
$self->{image}->$m($self->{gd_font}, $x, $y, $c->[0], $ctext); |
|
210 |
} |
|
211 |
return; |
|
212 |
} |
|
213 |
||
214 |
sub _insert_text_gd_normal { |
|
215 |
my($self, $key, $ctext) = @_; |
|
216 |
my $sw = $self->{gd_font}->width * length $key; |
|
217 |
my $sh = $self->{gd_font}->height; |
|
218 |
my $x = ($self->{width} - $sw) / 2; |
|
219 |
my $y = ($self->{height} - $sh) / 2; |
|
220 |
$self->{image}->string($self->{gd_font}, $x, $y, $key, $ctext); |
|
221 |
return; |
|
222 |
} |
|
223 |
||
224 |
sub insert_text { |
|
225 |
# Draw text using GD |
|
226 |
my $self = shift; |
|
227 |
my $method = shift; |
|
228 |
my $key = $self->{_RANDOM_NUMBER_}; |
|
229 |
my $ctext = $self->{_COLOR_}{text}; |
|
230 |
if ($method eq 'ttf') { |
|
231 |
$self->{scramble} ? $self->_insert_text_ttf_scramble( $key, $ctext ) |
|
232 |
: $self->_insert_text_ttf_normal( $key, $ctext ) |
|
233 |
; |
|
234 |
} |
|
235 |
else { |
|
236 |
$self->{scramble} ? $self->_insert_text_gd_scramble( $key, $ctext ) |
|
237 |
: $self->_insert_text_gd_normal( $key, $ctext ) |
|
238 |
; |
|
239 |
} |
|
240 |
return; |
|
200 |
241 |
} |
201 |
242 |
|
202 |
243 |
sub place_gd { |
203 |
my $self = shift; |
|
204 |
my($key, $tX, $tY) = @_; |
|
244 |
my($self, $key, $tx, $ty) = @_; |
|
205 |
245 |
my $tl = $self->{_TEXT_LOCATION_}; |
206 |
246 |
my $black = $self->cconvert($self->{_COLOR_}{text}); |
207 |
247 |
my $white = $self->cconvert($tl->{scolor}); |
208 |
248 |
my $font = GD::Font->Tiny; |
209 |
249 |
my $fx = (length($key)+1)*$font->width; |
210 |
250 |
my $x1 = $self->{width} - $fx; |
211 |
my $y1 = $tY eq 'up' ? 0 : $self->{height} - $font->height; |
|
212 |
if ($tY eq 'up') { |
|
213 |
|
|
251 |
my $y1 = $ty eq 'up' ? 0 : $self->{height} - $font->height; |
|
252 |
if ($ty eq 'up') { |
|
253 |
if($tx eq 'left') { |
|
214 |
254 |
$self->filledRectangle(0, $y1 , $fx , $font->height+2, $black); |
215 |
255 |
$self->filledRectangle(1, $y1+1, $fx-1, $font->height+1, $white); |
216 |
256 |
} |
| … | … | @@ -220,7 +260,7 @@ sub place_gd { |
220 |
260 |
} |
221 |
261 |
} |
222 |
262 |
else { |
223 |
if($t |
|
263 |
if($tx eq 'left') { |
|
224 |
264 |
$self->filledRectangle(0, $y1-2, $fx , $self->{height} , $black); |
225 |
265 |
$self->filledRectangle(1 , $y1-1, $fx-1, $self->{height}-2, $white); |
226 |
266 |
} |
| … | … | @@ -229,73 +269,127 @@ sub place_gd { |
229 |
269 |
$self->filledRectangle($x1-$font->width , $y1-1, $self->{width}-2, $self->{height}-2, $white); |
230 |
270 |
} |
231 |
271 |
} |
232 |
|
|
272 |
return $self->{image}->string( |
|
273 |
$font, |
|
274 |
$tx eq 'left' ? 2 : $x1, |
|
275 |
$ty eq 'up' ? $y1+1 : $y1-1, |
|
276 |
$key, |
|
277 |
$self->{_COLOR_}{text} |
|
278 |
); |
|
233 |
279 |
} |
234 |
280 |
|
235 |
281 |
sub ttf_info { |
236 |
282 |
my $self = shift; |
237 |
283 |
my $angle = shift || 0; |
238 |
284 |
my $text = shift; |
239 |
my $x = 0; |
|
240 |
my $y = 0; |
|
241 |
my @box = GD::Image->$TTF( |
|
242 |
$self->{_COLOR_}{text}, |
|
243 |
$self->{font}, |
|
244 |
$self->{ptsize}, |
|
245 |
Math::Trig::deg2rad($angle), |
|
246 |
0, |
|
247 |
0, |
|
248 |
$text |
|
249 |
|
|
285 |
require Math::Trig; |
|
286 |
my @box = GD::Image->$TTF( |
|
287 |
$self->{_COLOR_}{text}, |
|
288 |
$self->{font}, |
|
289 |
$self->{ptsize}, |
|
290 |
Math::Trig::deg2rad($angle), |
|
291 |
0, |
|
292 |
0, |
|
293 |
$text |
|
294 |
); |
|
250 |
295 |
if ( not @box ) { # use fake values instead of die-ing |
251 |
296 |
$self->{GDBOX_EMPTY} = 1; # set this for error checking. |
252 |
$#box |
|
297 |
$#box = BOX_SIZE; |
|
253 |
298 |
# lets initialize to silence the warnings |
254 |
299 |
$box[$_] = 1 for 0..$#box; |
255 |
300 |
} |
256 |
my $bx = $box[LOWLEFTX] - $box[LOWRIGHTX]; |
|
257 |
my $by = $box[LOWLEFTY] - $box[LOWRIGHTY]; |
|
258 |
301 |
|
259 |
if($angle == 0 or $angle == 180 or $angle == 360) { |
|
260 |
$by = $box[ UPLEFTY ] - $box[LOWLEFTY ]; |
|
261 |
} elsif ($angle == 90 or $angle == 270) { |
|
262 |
$bx = $box[ UPLEFTX ] - $box[LOWLEFTX ]; |
|
263 |
} elsif($angle > 270 and $angle < 360) { |
|
264 |
$bx = $box[ LOWLEFTX ] - $box[ UPLEFTX ]; |
|
265 |
} elsif ($angle > 180 and $angle < 270) { |
|
266 |
$by = $box[ LOWLEFTY ] - $box[LOWRIGHTY]; |
|
267 |
$bx = $box[ LOWRIGHTX] - $box[ UPRIGHTX]; |
|
268 |
} elsif($angle > 90 and $angle < 180) { |
|
269 |
$bx = $box[ LOWRIGHTX] - $box[ LOWLEFTX]; |
|
270 |
$by = $box[ LOWRIGHTY] - $box[ UPRIGHTY]; |
|
271 |
} elsif ($angle > 0 and $angle < 90) { |
|
272 |
$by = $box[ UPLEFTY ] - $box[ LOWLEFTY]; |
|
273 |
|
|
302 |
return $self->_ttf_info_xy( $angle, \@box ); |
|
303 |
} |
|
274 |
304 |
|
275 |
if ($angle == 0 ) { $x += $bx/2; $y -= $by/2; } |
|
276 |
elsif ($angle > 0 and $angle < 90 ) { $x += $bx/2; $y -= $by/2; } |
|
277 |
elsif ($angle == 90 ) { $x -= $bx/2; $y += $by/2; } |
|
278 |
elsif ($angle > 90 and $angle < 180) { $x -= $bx/2; $y += $by/2; } |
|
279 |
elsif ($angle == 180 ) { $x += $bx/2; $y -= $by/2; } |
|
280 |
elsif ($angle > 180 and $angle < 270) { $x += $bx/2; $y += $by/2; } |
|
281 |
elsif ($angle == 270 ) { $x -= $bx/2; $y += $by/2; } |
|
282 |
elsif ($angle > 270 and $angle < 360) { $x += $bx/2; $y += $by/2; } |
|
283 |
elsif ($angle == 360 ) { $x += $bx/2; $y -= $by/2; } |
|
305 |
sub _ttf_info_xy { |
|
306 |
my($self, $angle, $box) = @_; |
|
307 |
my $rnone = ROTATE_NONE; |
|
308 |
my $rccw = ROTATE_COUNTERCLOCKWISE; |
|
309 |
my $rusd = ROTATE_UPSIDEDOWN; |
|
310 |
my $rcw = ROTATE_CLOCKWISE; |
|
311 |
my $fc = FULL_CIRCLE; |
|
312 |
||
313 |
my $x = 0; |
|
314 |
my $y = 0; |
|
315 |
||
316 |
my($bx, $by) = $self->_ttf_info_box_xy( $angle, $box ); |
|
317 |
||
318 |
$angle == $rnone ? do { $x += $bx/2; $y -= $by/2; } |
|
319 |
: $angle > $rnone && $angle < $rccw ? do { $x += $bx/2; $y -= $by/2; } |
|
320 |
: $angle == $rccw ? do { $x -= $bx/2; $y += $by/2; } |
|
321 |
: $angle > $rccw && $angle < $rusd ? do { $x -= $bx/2; $y += $by/2; } |
|
322 |
: $angle == $rusd ? do { $x += $bx/2; $y -= $by/2; } |
|
323 |
: $angle > $rusd && $angle < $rcw ? do { $x += $bx/2; $y += $by/2; } |
|
324 |
: $angle == $rcw ? do { $x -= $bx/2; $y += $by/2; } |
|
325 |
: $angle > $rcw && $angle < $fc ? do { $x += $bx/2; $y += $by/2; } |
|
326 |
: $angle == $fc ? do { $x += $bx/2; $y -= $by/2; } |
|
327 |
: do {} |
|
328 |
; |
|
284 |
329 |
return $x, $y; |
285 |
330 |
} |
286 |
331 |
|
287 |
sub setPixel { shift->{image}->setPixel(@_) } |
|
288 |
sub line { shift->{image}->line(@_) } |
|
289 |
sub rectangle { shift->{image}->rectangle(@_) } |
|
290 |
sub filledRectangle { shift->{image}->filledRectangle(@_) } |
|
291 |
sub ellipse { shift->{image}->ellipse(@_) } |
|
292 |
sub arc { shift->{image}->arc(@_) } |
|
332 |
sub _ttf_info_box_xy { |
|
333 |
my($self, $angle, $box) = @_; |
|
334 |
my $bx = $box->[LOWLEFTX] - $box->[LOWRIGHTX]; |
|
335 |
my $by = $box->[LOWLEFTY] - $box->[LOWRIGHTY]; |
|
293 |
336 |
|
294 |
sub setThickness { |
|
295 |
my $self = shift; |
|
296 |
if($self->{image}->can('setThickness')) { # $GD::VERSION >= 2.07 |
|
297 |
$self->{image}->setThickness(@_); |
|
337 |
my $rnone = ROTATE_NONE; |
|
338 |
my $rccw = ROTATE_COUNTERCLOCKWISE; |
|
339 |
my $rusd = ROTATE_UPSIDEDOWN; |
|
340 |
my $rcw = ROTATE_CLOCKWISE; |
|
341 |
my $fc = FULL_CIRCLE; |
|
342 |
||
343 |
my $is_perp = $angle == $rnone || $angle == $rusd || $angle == $fc; |
|
344 |
||
345 |
$is_perp ? do { $by = $box->[ UPLEFTY ] - $box->[LOWLEFTY ]; } |
|
346 |
: $angle == $rccw || $angle == $rcw ? do { $bx = $box->[ UPLEFTX ] - $box->[LOWLEFTX ]; } |
|
347 |
: $angle > $rcw && $angle < $fc ? do { $bx = $box->[ LOWLEFTX ] - $box->[ UPLEFTX ]; } |
|
348 |
: $angle > $rusd && $angle < $rcw ? do { $bx = $box->[ LOWRIGHTX] - $box->[ UPRIGHTX]; $by = $box->[ LOWLEFTY ] - $box->[LOWRIGHTY]; } |
|
349 |
: $angle > $rccw && $angle < $rusd ? do { $bx = $box->[ LOWRIGHTX] - $box->[ LOWLEFTX]; $by = $box->[ LOWRIGHTY] - $box->[ UPRIGHTY]; } |
|
350 |
: $angle > $rnone && $angle < $rccw ? do { $by = $box->[ UPLEFTY ] - $box->[ LOWLEFTY]; } |
|
351 |
: do {} |
|
352 |
; |
|
353 |
||
354 |
return $bx, $by; |
|
355 |
} |
|
356 |
||
357 |
sub setPixel { ## no critic (NamingConventions::Capitalization) |
|
358 |
my($self, @args) = @_; |
|
359 |
return $self->{image}->setPixel(@args); |
|
360 |
} |
|
361 |
||
362 |
sub line { |
|
363 |
my($self, @args) = @_; |
|
364 |
return $self->{image}->line(@args); |
|
365 |
} |
|
366 |
||
367 |
sub rectangle { |
|
368 |
my($self, @args) = @_; |
|
369 |
return $self->{image}->rectangle(@args); |
|
370 |
} |
|
371 |
||
372 |
sub filledRectangle { ## no critic (NamingConventions::Capitalization) |
|
373 |
my($self, @args) = @_; |
|
374 |
return $self->{image}->filledRectangle(@args); |
|
375 |
} |
|
376 |
||
377 |
sub ellipse { |
|
378 |
my($self, @args) = @_; |
|
379 |
return $self->{image}->ellipse(@args); |
|
380 |
} |
|
381 |
||
382 |
sub arc { |
|
383 |
my($self, @args) = @_; |
|
384 |
return $self->{image}->arc(@args); |
|
385 |
} |
|
386 |
||
387 |
sub setThickness { ## no critic (NamingConventions::Capitalization) |
|
388 |
my($self, @args) = @_; |
|
389 |
if( $self->{image}->can('setThickness') ) { # $GD::VERSION >= 2.07 |
|
390 |
$self->{image}->setThickness( @args ); |
|
298 |
391 |
} |
392 |
return; |
|
299 |
393 |
} |
300 |
394 |
|
301 |
395 |
sub _versiongt { |
| … | … | @@ -362,18 +456,4 @@ Used internally by L<GD::SecurityImage>. |
362 |
456 |
|
363 |
457 |
L<GD::SecurityImage>. |
364 |
458 |
|
365 |
=head1 AUTHOR |
|
366 |
||
367 |
Burak GE<252>rsoy, E<lt>burakE<64>cpan.orgE<gt> |
|
368 |
||
369 |
=head1 COPYRIGHT |
|
370 |
||
371 |
Copyright 2004-2008 Burak GE<252>rsoy. All rights reserved. |
|
372 |
||
373 |
=head1 LICENSE |
|
374 |
||
375 |
This library is free software; you can redistribute it and/or modify |
|
376 |
it under the same terms as Perl itself, either Perl version 5.8.8 or, |
|
377 |
at your option, any later version of Perl 5 you may have available. |
|
378 |
||
379 |
459 |
=cut |
Up to file-list lib/GD/SecurityImage/Magick.pm:
1 |
1 |
package GD::SecurityImage::Magick; |
2 |
2 |
# GD method emulation class for Image::Magick |
3 |
3 |
use strict; |
4 |
use warnings; |
|
4 |
5 |
use vars qw($VERSION); |
5 |
6 |
# Magick related |
6 |
7 |
use constant XPPEM => 0; # character width |
| … | … | @@ -15,15 +16,16 @@ use constant ANGLE => -2; |
15 |
16 |
use constant CHAR => -1; |
16 |
17 |
# image data |
17 |
18 |
use constant MAX_COMPRESS => 100; |
19 |
use constant FULL_CIRCLE => 360; |
|
18 |
20 |
|
19 |
21 |
use Image::Magick; |
20 |
22 |
|
21 |
$VERSION = '1. |
|
23 |
$VERSION = '1.71'; |
|
22 |
24 |
|
23 |
25 |
sub init { |
24 |
26 |
# Create the image object |
25 |
27 |
my $self = shift; |
26 |
my $bg = $self->cconvert( $self->{bgcolor} ); |
|
28 |
my $bg = $self->cconvert( $self->{bgcolor} ); |
|
27 |
29 |
$self->{image} = Image::Magick->new; |
28 |
30 |
$self->{image}->Set( size=> "$self->{width}x$self->{height}" ); |
29 |
31 |
$self->{image}->Read( 'null:' . $bg ); |
| … | … | @@ -34,15 +36,15 @@ sub init { |
34 |
36 |
} |
35 |
37 |
|
36 |
38 |
sub out { |
37 |
my $self = shift; |
|
38 |
my %opt = scalar @_ % 2 ? () : (@_); |
|
39 |
my($self, @args) = @_; |
|
40 |
my %opt = @args % 2 ? () : @args; |
|
39 |
41 |
my $type = 'gif'; # default format |
40 |
42 |
if ($opt{force}) { |
41 |
my %g = map { $_ |
|
43 |
my %g = map { $_ => 1 } $self->{image}->QueryFormat; |
|
42 |
44 |
$type = $opt{force} if exists $g{$opt{force}}; |
43 |
45 |
} |
44 |
46 |
$self->{image}->Set( magick => $type ); |
45 |
if ( $opt{'compress'} && |
|
47 |
if ( $opt{'compress'} && ( $type eq 'png' || $type eq 'jpeg' ) ) { |
|
46 |
48 |
if($type eq 'png') { |
47 |
49 |
$opt{'compress'} = MAX_COMPRESS; |
48 |
50 |
$self->{image}->Set( compression => 'Zip' ); |
| … | … | @@ -72,12 +74,14 @@ sub insert_text { |
72 |
74 |
); |
73 |
75 |
|
74 |
76 |
if ($self->{scramble}) { |
75 |
my $space = [$info->( |
|
77 |
my $space = [$info->(q{ }), 0, q{ }]; # get " " parameters |
|
76 |
78 |
my @randomy; |
77 |
79 |
my $sy = $space->[ASCENDER] || 1; |
78 |
|
|
80 |
## no critic (ValuesAndExpressions::ProhibitMagicNumbers) |
|
81 |
push @randomy, $_, - $_ foreach $sy/2, $sy/4, $sy/8; |
|
82 |
## use critic |
|
79 |
83 |
my @char; |
80 |
foreach ( split |
|
84 |
foreach ( split m{}xms, $key ) { |
|
81 |
85 |
push @char, [$info->($_), $self->random_angle, $_], $space, $space, $space; |
82 |
86 |
} |
83 |
87 |
my $total = 0; |
| … | … | @@ -99,7 +103,7 @@ sub insert_text { |
99 |
103 |
my $tl = $self->{_TEXT_LOCATION_}; |
100 |
104 |
if ($tl->{_place_}) { |
101 |
105 |
# put the text to one of the four corners in the image |
102 |
$x = $tl->{x} eq 'left' ? 2 : $self->{width}-$metric[WIDTH] |
|
106 |
$x = $tl->{x} eq 'left' ? 2 : $self->{width}-$metric[WIDTH] - 2; |
|
103 |
107 |
$y = $tl->{y} eq 'up' ? $metric[ASCENDER]+1 : $self->{height}-2; |
104 |
108 |
$self->add_strip($x, $y, $metric[WIDTH], $metric[ASCENDER]) if $tl->{strip}; |
105 |
109 |
} |
| … | … | @@ -108,27 +112,25 @@ sub insert_text { |
108 |
112 |
$y = ($self->{height} + $metric[ASCENDER]) / 2; |
109 |
113 |
} |
110 |
114 |
$self->{image}->Annotate( |
111 |
text => $key, |
|
112 |
x => $x, |
|
115 |
text => $key, |
|
116 |
x => $x, |
|
113 |
117 |
y => $y, |
114 |
rotate => $self->{angle} ? |
|
118 |
rotate => $self->{angle} ? FULL_CIRCLE - $self->{angle} : 0, |
|
115 |
119 |
%same, |
116 |
120 |
); |
117 |
121 |
} |
118 |
122 |
return; |
119 |
123 |
} |
120 |
124 |
|
121 |
sub setPixel { |
|
122 |
my $self = shift; |
|
123 |
my($x, $y, $color) = @_; |
|
124 |
$self->{image}->Set( "pixel[$x,$y]" => $self->cconvert($color) ); |
|
125 |
sub setPixel { ## no critic (NamingConventions::Capitalization) |
|
126 |
my($self, $x, $y, $color) = @_; |
|
127 |
return $self->{image}->Set( "pixel[$x,$y]" => $self->cconvert($color) ); |
|
125 |
128 |
} |
126 |
129 |
|
127 |
130 |
sub line { |
128 |
my $self = shift; |
|
129 |
my($x1, $y1, $x2, $y2, $color) = @_; |
|
130 |
$self->{image}->Draw( |
|
131 |
primitive => "line", |
|
131 |
my($self, $x1, $y1, $x2, $y2, $color) = @_; |
|
132 |
return $self->{image}->Draw( |
|
133 |
primitive => 'line', |
|
132 |
134 |
points => "$x1,$y1 $x2,$y2", |
133 |
135 |
stroke => $self->cconvert($color), |
134 |
136 |
strokewidth => $self->{MAGICK}{strokewidth}, |
| … | … | @@ -136,10 +138,9 @@ sub line { |
136 |
138 |
} |
137 |
139 |
|
138 |
140 |
sub rectangle { |
139 |
my $self = shift; |
|
140 |
my($x1,$y1,$x2,$y2,$color) = @_; |
|
141 |
$self->{image}->Draw( |
|
142 |
primitive => "rectangle", |
|
141 |
my($self, $x1,$y1,$x2,$y2,$color) = @_; |
|
142 |
return $self->{image}->Draw( |
|
143 |
primitive => 'rectangle', |
|
143 |
144 |
points => "$x1,$y1 $x2,$y2", |
144 |
145 |
stroke => $self->cconvert($color), |
145 |
146 |
strokewidth => $self->{MAGICK}{strokewidth}, |
| … | … | @@ -147,11 +148,10 @@ sub rectangle { |
147 |
148 |
); |
148 |
149 |
} |
149 |
150 |
|
150 |
sub filledRectangle { |
|
151 |
my $self = shift; |
|
152 |
my($x1,$y1,$x2,$y2,$color) = @_; |
|
153 |
$self->{image}->Draw( |
|
154 |
primitive => "rectangle", |
|
151 |
sub filledRectangle { ## no critic (NamingConventions::Capitalization) |
|
152 |
my($self, $x1, $y1, $x2, $y2, $color) = @_; |
|
153 |
return $self->{image}->Draw( |
|
154 |
primitive => 'rectangle', |
|
155 |
155 |
points => "$x1,$y1 $x2,$y2", |
156 |
156 |
fill => $self->cconvert($color), |
157 |
157 |
stroke => $self->cconvert($color), |
| … | … | @@ -160,10 +160,9 @@ sub filledRectangle { |
160 |
160 |
} |
161 |
161 |
|
162 |
162 |
sub ellipse { |
163 |
my $self = shift; |
|
164 |
my($cx,$cy,$width,$height,$color) = @_; |
|
165 |
$self->{image}->Draw( |
|
166 |
primitive => "ellipse", |
|
163 |
my($self, $cx, $cy, $width, $height, $color) = @_; |
|
164 |
return $self->{image}->Draw( |
|
165 |
primitive => 'ellipse', |
|
167 |
166 |
points => "$cx,$cy $width,$height 0,360", |
168 |
167 |
stroke => $self->cconvert($color), |
169 |
168 |
strokewidth => $self->{MAGICK}{strokewidth}, |
| … | … | @@ -172,12 +171,11 @@ sub ellipse { |
172 |
171 |
} |
173 |
172 |
|
174 |
173 |
sub arc { |
175 |
my $self = shift; |
|
176 |
my($cx,$cy,$width,$height,$start,$end,$color) = @_; |
|
174 |
my($self, $cx, $cy, $width, $height, $start, $end, $color) = @_; |
|
177 |
175 |
# I couldn't do that with "arc" primitive. |
178 |
176 |
# Patches are welcome, but this seems to work :) |
179 |
$self->{image}->Draw( |
|
180 |
primitive => "ellipse", |
|
177 |
return $self->{image}->Draw( |
|
178 |
primitive => 'ellipse', |
|
181 |
179 |
points => "$cx,$cy $width,$height $start,$end", |
182 |
180 |
stroke => $self->cconvert($color), |
183 |
181 |
strokewidth => $self->{MAGICK}{strokewidth}, |
| … | … | @@ -185,7 +183,7 @@ sub arc { |
185 |
183 |
); |
186 |
184 |
} |
187 |
185 |
|
188 |
sub setThickness { |
|
186 |
sub setThickness { ## no critic (NamingConventions::Capitalization) |
|
189 |
187 |
my $self = shift; |
190 |
188 |
my $thickness = shift || return; |
191 |
189 |
$self->{MAGICK}{strokewidth} *= $thickness; |
| … | … | @@ -211,8 +209,8 @@ sub _versionlt { |
211 |
209 |
sub _tovstr { |
212 |
210 |
my $self = shift; |
213 |
211 |
my $thing = shift || return '0.0.0'; |
214 |
my @j = split /\./, $thing; |
|
215 |
my $rv = join '.', |
|
212 |
my @j = split m{[.]}xms, $thing; |
|
213 |
my $rv = join q{.}, |
|
216 |
214 |
shift(@j) || 0, |
217 |
215 |
shift(@j) || 0, |
218 |
216 |
shift(@j) || 0, |
| … | … | @@ -220,6 +218,8 @@ sub _tovstr { |
220 |
218 |
return $rv; |
221 |
219 |
} |
222 |
220 |
|
221 |
sub gdbox_empty { return 0 } |
|
222 |
||
223 |
223 |
1; |
224 |
224 |
|
225 |
225 |
__END__ |
| … | … | @@ -260,22 +260,10 @@ Used internally by L<GD::SecurityImage>. |
260 |
260 |
|
261 |
261 |
=head2 setThickness |
262 |
262 |
|
263 |
=head2 gdbox_empty |
|
264 |
||
263 |
265 |
=head1 SEE ALSO |
264 |
266 |
|
265 |
267 |
L<GD::SecurityImage>. |
266 |
268 |
|
267 |
=head1 AUTHOR |
|
268 |
||
269 |
Burak GE<252>rsoy, E<lt>burakE<64>cpan.orgE<gt> |
|
270 |
||
271 |
=head1 COPYRIGHT |
|
272 |
||
273 |
Copyright 2004-2008 Burak GE<252>rsoy. All rights reserved. |
|
274 |
||
275 |
=head1 LICENSE |
|
276 |
||
277 |
This library is free software; you can redistribute it and/or modify |
|
278 |
it under the same terms as Perl itself, either Perl version 5.8.8 or, |
|
279 |
at your option, any later version of Perl 5 you may have available. |
|
280 |
||
281 |
269 |
=cut |
Up to file-list lib/GD/SecurityImage/Styles.pm:
1 |
1 |
package GD::SecurityImage::Styles; |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw[$VERSION]; |
5 |
use constant ARC_END_DEGREES => 360; |
|
4 |
6 |
|
5 |
$VERSION = '1. |
|
7 |
$VERSION = '1.71'; |
|
6 |
8 |
|
7 |
9 |
sub style_default { |
8 |
|
|
10 |
return shift->_drcommon(' \\ lines will be drawn '); |
|
9 |
11 |
} |
10 |
12 |
|
11 |
13 |
sub style_rect { |
12 |
|
|
14 |
return shift->_drcommon; |
|
13 |
15 |
} |
14 |
16 |
|
15 |
17 |
sub style_box { |
| … | … | @@ -21,6 +23,7 @@ sub style_box { |
21 |
23 |
my $h = $self->{height}; |
22 |
24 |
$self->filledRectangle( 0, 0, $w , $h , $ct ); |
23 |
25 |
$self->filledRectangle( $n, $n, $w - $n - 1, $h - $n - 1, $cl ); |
26 |
return; |
|
24 |
27 |
} |
25 |
28 |
|
26 |
29 |
sub style_circle { |
| … | … | @@ -32,11 +35,11 @@ sub style_circle { |
32 |
35 |
my $max = int $self->{width} / $n; |
33 |
36 |
$max++; |
34 |
37 |
|
35 |
my( $i, $mi ); |
|
36 |
for $i ( 1..$n ) { |
|
37 |
$mi = $max * $i; |
|
38 |
$self->arc( $cx, $cy, $mi, $mi, 0, 360, $cl ); |
|
38 |
for my $i ( 1..$n ) { |
|
39 |
my $mi = $max * $i; |
|
40 |
$self->arc( $cx, $cy, $mi, $mi, 0, ARC_END_DEGREES, $cl ); |
|
39 |
41 |
} |
42 |
return; |
|
40 |
43 |
} |
41 |
44 |
|
42 |
45 |
sub style_ellipse { |
| … | … | @@ -49,17 +52,18 @@ sub style_ellipse { |
49 |
52 |
my $max = int $self->{width} / $n; |
50 |
53 |
$max++; |
51 |
54 |
|
52 |
my( $i, $mi ); |
|
53 |
for $i ( 1..$n ) { |
|
54 |
|
|
55 |
for my $i ( 1..$n ) { |
|
56 |
my $mi = $max * $i; |
|
55 |
57 |
$self->ellipse( $cx, $cy, $mi * 2, $mi, $cl ); |
56 |
58 |
} |
59 |
return; |
|
57 |
60 |
} |
58 |
61 |
|
59 |
62 |
sub style_ec { |
60 |
my $self = shift; |
|
61 |
$self->style_ellipse(@_) if not $self->{DISABLED}{ellipse}; # GD < 2.07 |
|
62 |
|
|
63 |
my($self, @args) = @_; |
|
64 |
$self->style_ellipse(@args) if ! $self->{DISABLED}{ellipse}; # GD < 2.07 |
|
65 |
$self->style_circle(@args); |
|
66 |
return; |
|
63 |
67 |
} |
64 |
68 |
|
65 |
69 |
sub style_blank {} |
| … | … | @@ -87,6 +91,7 @@ sub _drcommon { |
87 |
91 |
$ify = $i * $fy; |
88 |
92 |
$self->line( 0, $ify, $w, $ify, $cl ); # - line |
89 |
93 |
} |
94 |
return; |
|
90 |
95 |
} |
91 |
96 |
|
92 |
97 |
1; |
| … | … | @@ -127,18 +132,4 @@ Used internally by L<GD::SecurityImage>. |
127 |
132 |
|
128 |
133 |
L<GD::SecurityImage>. |
129 |
134 |
|
130 |
=head1 AUTHOR |
|
131 |
||
132 |
Burak GE<252>rsoy, E<lt>burakE<64>cpan.orgE<gt> |
|
133 |
||
134 |
=head1 COPYRIGHT |
|
135 |
||
136 |
Copyright 2004-2008 Burak GE<252>rsoy. All rights reserved. |
|
137 |
||
138 |
=head1 LICENSE |
|
139 |
||
140 |
This library is free software; you can redistribute it and/or modify |
|
141 |
it under the same terms as Perl itself, either Perl version 5.8.8 or, |
|
142 |
at your option, any later version of Perl 5 you may have available. |
|
143 |
||
144 |
135 |
=cut |
Up to file-list t/03-info_text.t:
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use |
|
3 |
use warnings; |
|
4 |
use Test::More qw(no_plan); |
|
4 |
5 |
use Cwd; |
5 |
6 |
use GD::SecurityImage; |
6 |
7 |
|
7 |
plan tests => 1; |
|
8 |
||
9 |
8 |
my $i = GD::SecurityImage->new->random; |
10 |
9 |
my $random = $i->random_str; |
11 |
10 |
$i->create; |
| … | … | @@ -19,4 +18,4 @@ my $random = $i->random_str; |
19 |
18 |
|
20 |
19 |
my( $image, $mime, $random2 ) = $i->out; |
21 |
20 |
|
22 |
ok( $random eq $random2 |
|
21 |
ok( $random eq $random2, 'info_text must not affect random string' ); |
Up to file-list t/04-backend.t:
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw( $MAGICK_SKIP ); |
4 |
use Test |
|
5 |
use Test::More; |
|
5 |
6 |
use Cwd; |
7 |
use Carp qw( croak ); |
|
8 |
use lib qw( .. ); |
|
6 |
9 |
|
7 |
10 |
BEGIN { |
8 |
do 't/magick.pl' || |
|
11 |
do 't/magick.pl' || croak "Can not include t/magick.pl: $!"; |
|
9 |
12 |
|
10 |
13 |
my %total = ( |
11 |
14 |
magick => 2, |
| … | … | @@ -15,29 +18,32 @@ BEGIN { |
15 |
18 |
|
16 |
19 |
my $total = 0; |
17 |
20 |
$total += $total{$_} foreach keys %total; |
18 |
my $class |
|
21 |
my $class = 'GD::SecurityImage'; |
|
19 |
22 |
|
20 |
23 |
plan tests => $total; |
21 |
24 |
|
22 |
25 |
require GD::SecurityImage; |
23 |
26 |
|
24 |
eval { $class->new }; |
|
25 |
ok($@); # if there is an error == OK [since we didn't import() so far] |
|
27 |
my $eok = eval { $class->new }; |
|
28 |
ok( $@, q{If there is an error == OK [since we didn't import() so far]} ); |
|
26 |
29 |
|
27 |
30 |
# test if we've loaded the right library |
28 |
gd(); |
|
29 |
$MAGICK_SKIP ? skip_magick() : magick(); |
|
30 |
|
|
31 |
GD_TEST: { |
|
32 |
$class->import( use_magick => 0 ); |
|
33 |
ok( $class->new->raw->isa('GD::Image' ), 'Loaded GD [1]' ); |
|
34 |
$class->import( backend => 'GD' ); |
|
35 |
ok( $class->new->raw->isa('GD::Image' ), 'Loaded GD [2]' ); |
|
36 |
} |
|
31 |
37 |
|
32 |
sub gd { |
|
33 |
$class->import( use_magick => 0 ); ok( $class->new->raw->isa('GD::Image' ) ); |
|
34 |
$class->import( backend => 'GD' ); ok( $class->new->raw->isa('GD::Image' ) ); |
|
35 |
} |
|
36 |
sub magick { |
|
37 |
$class->import( use_magick => 1 ); ok( $class->new->raw->isa('Image::Magick') ); |
|
38 |
$class->import( backend => 'Magick' ); ok( $class->new->raw->isa('Image::Magick') ); |
|
39 |
} |
|
40 |
sub skip_magick { |
|
41 |
skip( $MAGICK_SKIP . " Skipping...", sub{1}) for 1..$total{magick}; |
|
42 |
|
|
38 |
$MAGICK_SKIP |
|
39 |
? do { |
|
40 |
skip( $MAGICK_SKIP . ' Skipping...', sub{1}) for 1..$total{magick}; |
|
41 |
} |
|
42 |
: do { |
|
43 |
$class->import( use_magick => 1 ); |
|
44 |
ok( $class->new->raw->isa('Image::Magick'), 'Loaded Magick [1]' ); |
|
45 |
$class->import( backend => 'Magick' ); |
|
46 |
ok( $class->new->raw->isa('Image::Magick'), 'Loaded Magick [2]' ); |
|
47 |
} |
|
48 |
; |
|
43 |
49 |
} |
Up to file-list t/05-version.t:
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use |
|
3 |
use warnings; |
|
4 |
use Test::More; |
|
4 |
5 |
use Cwd; |
5 |
6 |
use GD::SecurityImage; |
6 |
7 |
|
| … | … | @@ -8,14 +9,14 @@ plan tests => 5; |
8 |
9 |
|
9 |
10 |
my $i = GD::SecurityImage->new; |
10 |
11 |
|
11 |
my $gt = $i->_versiongt(2.0); |
|
12 |
my $lt = $i->_versionlt(3.0); |
|
13 |
ok( defined $gt ); |
|
14 |
ok( defined $lt ); |
|
12 |
my $gt = $i->_versiongt('2.0'); |
|
13 |
my $lt = $i->_versionlt('3.0'); |
|
14 |
ok( defined $gt, 'GT defined' ); |
|
15 |
ok( defined $lt, 'LT defined' ); |
|
15 |
16 |
|
16 |
17 |
GT: { |
17 |
18 |
local $GD::VERSION = '1.19'; |
18 |
ok( $i->_versiongt(1.18) ); |
|
19 |
ok( $i->_versiongt(1.19) ); # ok. _versiongt() if greater or equal to |
|
20 |
ok( $i->_version |
|
19 |
ok( $i->_versiongt('1.18'), 'GT 1.18' ); |
|
20 |
ok( $i->_versiongt('1.19'), 'ok. _versiongt() if greater or equal to 1.19' ); |
|
21 |
ok( $i->_versionlt('3.0' ), 'but this means "smaller than"' ); |
|
21 |
22 |
} |
Up to file-list t/06-version_magick.t:
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw( $MAGICK_SKIP ); |
4 |
use Test |
|
5 |
use Test::More; |
|
5 |
6 |
use Cwd; |
7 |
use Carp qw( croak ); |
|
8 |
use lib qw( .. ); |
|
9 |
use constant TOTAL_TESTS => 6; |
|
6 |
10 |
|
7 |
11 |
BEGIN { |
8 |
do 't/magick.pl' || |
|
12 |
do 't/magick.pl' || croak "Can not include t/magick.pl: $!"; |
|
9 |
13 |
|
10 |
my $TOTAL = 6; |
|
11 |
plan tests => $TOTAL; |
|
14 |
plan tests => TOTAL_TESTS; |
|
12 |
15 |
|
13 |
16 |
if ( $MAGICK_SKIP ) { |
14 |
skip( $MAGICK_SKIP . |
|
17 |
skip( $MAGICK_SKIP . ' Skipping...', sub{1}) for 1..TOTAL_TESTS; |
|
15 |
18 |
exit; |
16 |
19 |
} |
17 |
20 |
else { |
| … | … | @@ -22,15 +25,15 @@ BEGIN { |
22 |
25 |
|
23 |
26 |
my $i = GD::SecurityImage->new; |
24 |
27 |
|
25 |
my $gt = $i->_versiongt( |
|
28 |
my $gt = $i->_versiongt('6.0'); |
|
26 |
29 |
my $lt = $i->_versionlt('6.4.3'); |
27 |
ok( defined $gt ); |
|
28 |
ok( defined $lt ); |
|
30 |
ok( defined $gt, 'GT defined' ); |
|
31 |
ok( defined $lt, 'LT defined' ); |
|
29 |
32 |
|
30 |
33 |
GT: { |
31 |
34 |
local $Image::Magick::VERSION = '6.0.3'; |
32 |
ok( $i->_versiongt( 6.0 ) ); |
|
33 |
ok( $i->_versiongt( '6.0.3' ) ); |
|
34 |
ok( $i->_versionlt( 6.2 ) ); |
|
35 |
ok( $i->_versionlt( '6.2.6' ) ); |
|
35 |
ok( $i->_versiongt( '6.0' ), 'GT 6.0' ); |
|
36 |
ok( $i->_versiongt( '6.0.3' ), 'GT 6.0.3' ); |
|
37 |
ok( $i->_versionlt( '6.2' ), 'LT 6.2' ); |
|
38 |
ok( $i->_versionlt( '6.2.6' ), 'LT 6.2.6' ); |
|
36 |
39 |
} |
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw( %API ); |
4 |
use Test |
|
5 |
use Test::More; |
|
5 |
6 |
use Cwd; |
7 |
use Carp qw(croak); |
|
8 |
use lib qw( |
|
9 |
.. |
|
10 |
../t/lib |
|
11 |
t/lib |
|
12 |
); |
|
6 |
13 |
|
7 |
14 |
BEGIN { |
8 |
15 |
%API = ( |
| … | … | @@ -24,8 +31,9 @@ BEGIN { |
24 |
31 |
import GD::SecurityImage; |
25 |
32 |
} |
26 |
33 |
|
27 |
require 't/t.api'; |
|
28 |
my $tapi = 'tapi'; |
|
34 |
use Test::GDSI; |
|
35 |
||
36 |
my $tapi = 'Test::GDSI'; |
|
29 |
37 |
$tapi->clear; |
30 |
38 |
|
31 |
39 |
my $font = getcwd.'/StayPuft.ttf'; |
| … | … | @@ -50,7 +58,8 @@ foreach my $api (keys %API) { |
50 |
58 |
$style, |
51 |
59 |
$api, |
52 |
60 |
$c++ |
53 |
) |
|
61 |
), |
|
62 |
"$style - $api - $c++" |
|
54 |
63 |
); |
55 |
64 |
} |
56 |
65 |
$tapi->clear; |
| … | … | @@ -118,6 +127,6 @@ sub args { |
118 |
127 |
(my $tmp = $name) =~ s{ _info_text }{}xms; |
119 |
128 |
$o = $options{$tmp}; |
120 |
129 |
} |
121 |
|
|
130 |
croak "Bogus arg name $name!" if not $o; |
|
122 |
131 |
return %{$o} |
123 |
132 |
} |
Up to file-list t/99-magick.t:
1 |
1 |
#!/usr/bin/env perl -w |
2 |
2 |
use strict; |
3 |
use warnings; |
|
3 |
4 |
use vars qw( %API $MAGICK_SKIP ); |
4 |
use Test |
|
5 |
use Test::More; |
|
5 |
6 |
use Cwd; |
7 |
use Carp qw(croak); |
|
8 |
use lib qw( |
|
9 |
.. |
|
10 |
../t/lib |
|
11 |
t/lib |
|
12 |
); |
|
6 |
13 |
|
7 |
14 |
BEGIN { |
8 |
do 't/magick.pl' || |
|
15 |
do 't/magick.pl' || croak "Can not include t/magick.pl: $!"; |
|
9 |
16 |
|
10 |
17 |
%API = ( |
11 |
18 |
magick => 6, |
| … | … | @@ -22,7 +29,7 @@ BEGIN { |
22 |
29 |
plan tests => $total; |
23 |
30 |
|
24 |
31 |
if ( $MAGICK_SKIP ) { |
25 |
skip( $MAGICK_SKIP . |
|
32 |
skip( $MAGICK_SKIP . ' Skipping...', sub{1}) for 1..$total; |
|
26 |
33 |
exit; |
27 |
34 |
} |
28 |
35 |
else { |
| … | … | @@ -31,8 +38,9 @@ BEGIN { |
31 |
38 |
} |
32 |
39 |
} |
33 |
40 |
|
34 |
require 't/t.api'; |
|
35 |
my $tapi = 'tapi'; |
|
41 |
use Test::GDSI; |
|
42 |
||
43 |
my $tapi = 'Test::GDSI'; |
|
36 |
44 |
$tapi->clear; |
37 |
45 |
|
38 |
46 |
my $font = getcwd.'/StayPuft.ttf'; |
| … | … | @@ -57,7 +65,8 @@ foreach my $api (keys %API) { |
57 |
65 |
$style, |
58 |
66 |
$api, |
59 |
67 |
$c++ |
60 |
) |
|
68 |
), |
|
69 |
"$style - $api - $c++" |
|
61 |
70 |
); |
62 |
71 |
} |
63 |
72 |
$tapi->clear; |
| … | … | @@ -104,6 +113,6 @@ sub args { |
104 |
113 |
(my $tmp = $name) =~ s{ _info_text }{}xms; |
105 |
114 |
$o = $options{$tmp}; |
106 |
115 |
} |
107 |
|
|
116 |
croak "Bogus arg name $name!" if not $o; |
|
108 |
117 |
return %{ $o } |
109 |
118 |
} |
Up to file-list t/lib/Test/GDSI.pm:
1 |
## no critic (NamingConventions::Capitalization, ValuesAndExpressions::ProhibitMagicNumbers) |
|
2 |
package Test::GDSI; |
|
3 |
use strict; |
|
4 |
use warnings; |
|
5 |
use vars qw( $VERSION ); |
|
6 |
||
7 |
$VERSION = '0.40'; |
|
8 |
||
9 |
use constant GD => defined($GD::VERSION) ? $GD::VERSION : 0; |
|
10 |
use constant MAGICK => defined($Image::Magick::VERSION) ? $Image::Magick::VERSION : '0.0.0'; |
|
11 |
use constant PROBLEM => GD && GD < 2.07 ? 1 : 0; |
|
12 |
use Carp qw(croak); |
|
13 |
||
14 |
my(%options, %info_text); |
|
15 |
||
16 |
sub the_info_text { return 'GD::SecurityImage' } |
|
17 |
||
18 |
sub GD::SecurityImage::CIT { # __check_info_text |
|
19 |
my $self = shift; |
|
20 |
$self->info_text( %info_text ) if %info_text; |
|
21 |
return $self; |
|
22 |
} |
|
23 |
||
24 |
sub styles { |
|
25 |
return qw( default rect box circle ellipse ec ); |
|
26 |
} |
|
27 |
||
28 |
sub options { |
|
29 |
my($class, @args) = @_; |
|
30 |
%options = @args if @args; |
|
31 |
if ( $options{info_text} ) { |
|
32 |
%info_text = %{ delete $options{info_text} }; |
|
33 |
} |
|
34 |
return %options; |
|
35 |
} |
|
36 |
||
37 |
sub has_method { |
|
38 |
my $self = shift; |
|
39 |
my $name = shift || q{}; |
|
40 |
if ( PROBLEM && ($name eq 'ellipse' or $name eq 'ec') ) { |
|
41 |
return 'circle'; |
|
42 |
} |
|
43 |
return $name; |
|
44 |
} |
|
45 |
||
46 |
sub clear { |
|
47 |
%options = (); |
|
48 |
%info_text = (); |
|
49 |
return; |
|
50 |
} |
|
51 |
||
52 |
sub set_options { |
|
53 |
my($class, @args) = @_; |
|
54 |
my %o = @args % 2 ? () : @args; |
|
55 |
foreach ( keys %o ) { |
|
56 |
next if $_ eq 'thickness' && PROBLEM; |
|
57 |
$options{$_} = $o{$_}; |
|
58 |
} |
|
59 |
return $class; |
|
60 |
} |
|
61 |
||
62 |
sub random { |
|
63 |
return { |
|
64 |
ec => 'EC0123', |
|
65 |
ellipse => 'ELLIPS', |
|
66 |
circle => 'CIRCLE', |
|
67 |
box => 'BOX012', |
|
68 |
rect => 'RECT01', |
|
69 |
default => 'DFAULT', |
|
70 |
} |
|
71 |
} |
|
72 |
||
73 |
sub save { |
|
74 |
my($class, $image, $mime, $random, $style, $ID, $counter) = @_; |
|
75 |
my $name = sprintf '%s_%02d_%s.%s', $ID, $counter, $style, $mime; |
|
76 |
require IO::File; |
|
77 |
my $SI = IO::File->new; |
|
78 |
$SI->open( $name, '>' ) or croak "Error writing the image '$name' to disk: $!"; |
|
79 |
binmode $SI; |
|
80 |
print {$SI} $image or croak "Unable to print to $name"; |
|
81 |
close $SI or croak "Unable to close $name"; |
|
82 |
print "[OK] $name\n" or croak 'Unable to print to STDOUT'; |
|
83 |
return 'SUCCESS'; |
|
84 |
} |
|
85 |
||
86 |
package gd_normal; |
|
87 |
||
88 |
sub ec { |
|
89 |
return GD::SecurityImage |
|
90 |
->new( |
|
91 |
lines => 5, |
|
92 |
bgcolor => [0,0,0], |
|
93 |
Test::GDSI->options, |
|
94 |
) |
|
95 |
->random( Test::GDSI->random->{ec} ) |
|
96 |
->create( qw( normal ec ), [84, 207, 112], [0,0,0] ) |
|
97 |
->particle( 100 ) |
|
98 |
->CIT |
|
99 |
} |
|
100 |
||
101 |
sub ellipse { |
|
102 |
return GD::SecurityImage |
|
103 |
->new(lines => 10, bgcolor => [208, 202, 206], Test::GDSI->options) |
|
104 |
->random(Test::GDSI->random->{ellipse}) |
|
105 |
->create(normal => Test::GDSI->has_method('ellipse'), [31,219,180], [231,219,180]) |
|
106 |
->particle(100)->CIT |
|
107 |
} |
|
108 |
||
109 |
sub circle { |
|
110 |
return GD::SecurityImage |
|
111 |
->new(lines => 5, bgcolor => [210, 215, 196], Test::GDSI->options) |
|
112 |
->random(Test::GDSI->random->{circle}) |
|
113 |
->create(normal => 'circle', [63, 143, 167], [90, 195, 176]) |
|
114 |
->particle(250, 2)->CIT |
|
115 |
} |
|
116 |
||
117 |
sub box { |
|
118 |
return GD::SecurityImage |
|
119 |
->new(lines => 5, Test::GDSI->options) |
|
120 |
->random(Test::GDSI->random->{box}) |
|
121 |
->create(normal => 'box', [63, 143, 167], [226, 223, 169]) |
|
122 |
->particle(150, 4)->CIT |
|
123 |
} |
|
124 |
||
125 |
sub rect { |
|
126 |
return GD::SecurityImage |
|
127 |
->new(lines => 10, Test::GDSI->options) |
|
128 |
->random(Test::GDSI->random->{rect}) |
|
129 |
->create(normal => 'rect', [63, 143, 167], [226, 223, 169]) |
|
130 |
->particle(100)->CIT |
|
131 |
} |
|
132 |
||
133 |
sub default { ## no critic (Subroutines::ProhibitBuiltinHomonyms) |
|
134 |
return GD::SecurityImage |
|
135 |
->new(lines => 10, Test::GDSI->options, send_ctobg => 0) |
|
136 |
->random(Test::GDSI->random->{default}) |
|
137 |
->create(normal => 'default', [68,150,125], [255,0,0]) |
|
138 |
->particle(500)->CIT |
|
139 |
} |
|
140 |
||
141 |
package gd_ttf; |
|
142 |
||
143 |
sub ec { |
|
144 |
return GD::SecurityImage |
|
145 |
->new(lines => 16, bgcolor => [0,0,0], Test::GDSI->set_options(thickness => 1)->options) |
|
146 |
->random(Test::GDSI->random->{ec}) |
|
147 |
->create(ttf => 'ec', [84, 207, 112], [0,0,0]) |
|
148 |
->particle(1000)->CIT |
|
149 |
} |
|
150 |
||
151 |
sub ellipse { |
|
152 |
return GD::SecurityImage |
|
153 |
->new(lines => 15, bgcolor => [208, 202, 206], Test::GDSI->set_options(thickness => 2)->options) |
|
154 |
->random(Test::GDSI->random->{ellipse}) |
|
155 |
->create(ttf => Test::GDSI->has_method('ellipse'), [184,20,180], [184,20,180]) |
|
156 |
->particle(2000)->CIT |
|
157 |
} |
|
158 |
||
159 |
sub circle { |
|
160 |
return GD::SecurityImage |
|
161 |
->new(lines => 50, bgcolor => [210, 215, 196],Test::GDSI->set_options(thickness => 1)->options) |
|
162 |
->random(Test::GDSI->random->{circle}) |
|
163 |
->create(ttf => 'circle', [63, 143, 167], [210, 215, 196]) |
|
164 |
->particle(3500)->CIT |
|
165 |
} |
|
166 |
||
167 |
sub box { |
|
168 |
return GD::SecurityImage |
|
169 |
->new(lines => 6, Test::GDSI->set_options(thickness => 1)->options, frame => 0) |
|
170 |
->random(Test::GDSI->random->{box}) |
|
171 |
->create(ttf => 'box', [245,240,220], [115, 115, 115]) |
|
172 |
->particle(3000, 2)->CIT |
|
173 |
} |
|
174 |
||
175 |
sub rect { |
|
176 |
return GD::SecurityImage |
|
177 |
->new(lines => 30, Test::GDSI->set_options(thickness => 1)->options) |
|
178 |
->random(Test::GDSI->random->{rect}) |
|
179 |
->create(ttf => 'rect', [63, 143, 167], [226, 223, 169]) |
|
180 |
->particle(2000)->CIT |
|
181 |
} |
|
182 |
||
183 |
sub default { ## no critic (ProhibitBuiltinHomonyms) |
|
184 |
return GD::SecurityImage |
|
185 |
->new(lines => 10, Test::GDSI->set_options(thickness => 2)->options) |
|
186 |
->random(Test::GDSI->random->{default}) |
|
187 |
->create(ttf => 'default', [68,150,125], [255,0,0]) |
|
188 |
->particle(5000)->CIT |
|
189 |
} |
|
190 |
||
191 |
package gd_normal_scramble; use base qw(gd_normal); |
|
192 |
package gd_ttf_scramble; use base qw(gd_ttf); |
|
193 |
package gd_ttf_scramble_fixed; use base qw(gd_ttf); |
|
194 |
package gd_normal_info_text; use base qw(gd_normal); |
|
195 |
package gd_ttf_info_text; use base qw(gd_ttf); |
|
196 |
package gd_normal_scramble_info_text; use base qw(gd_normal); |
|
197 |
package gd_ttf_scramble_info_text; use base qw(gd_ttf); |
|
198 |
package gd_ttf_scramble_fixed_info_text; use base qw(gd_ttf); |
|
199 |
||
200 |
package magick; use base qw(gd_ttf); |
|
201 |
package magick_scramble; use base qw(gd_ttf); |
|
202 |
package magick_scramble_fixed; use base qw(gd_ttf); |
|
203 |
package magick_info_text; use base qw(gd_ttf); |
|
204 |
package magick_scramble_info_text; use base qw(gd_ttf); |
|
205 |
package magick_scramble_fixed_info_text; use base qw(gd_ttf); |
|
206 |
||
207 |
1; |
|
208 |
||
209 |
__END__ |
1 |
1 |
use strict; |
2 |
use |
|
2 |
use warnings; |
|
3 |
use vars qw( $MAGICK_SKIP ); |
|
4 |
||
3 |
5 |
BEGIN { |
4 |
eval "require Image::Magick;"; |
|
5 |
if ( $@ ) { |
|
6 |
$MAGICK_SKIP = "You don't have Image::Magick installed."; |
|
7 |
$MAGICK_SKIP .= " $@"; |
|
6 |
my $eok = eval { |
|
7 |
require Image::Magick; |
|
8 |
1; |
|
9 |
}; |
|
10 |
if ( $@ || ! $eok ) { |
|
11 |
$MAGICK_SKIP = "You don't have Image::Magick installed. $@"; |
|
8 |
12 |
} |
9 |
13 |
elsif ( $Image::Magick::VERSION lt '6.0.4') { |
10 |
$MAGICK_SKIP = |
|
14 |
$MAGICK_SKIP = q{There may be a bug in your PerlMagick version's } |
|
11 |
15 |
. "($Image::Magick::VERSION) QueryFontMetrics() method. " |
12 |
. |
|
16 |
. q{Please upgrade to 6.0.4 or newer.}; |
|
13 |
17 |
} |
14 |
18 |
else { |
15 |
$MAGICK_SKIP = |
|
19 |
$MAGICK_SKIP = q{}; |
|
16 |
20 |
} |
17 |
21 |
} |
18 |
22 |
