File Coverage

blib/lib/Parse/WebCounter.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # ======================================================================
2             # Project: Web Counter Parser
3             # Project Leader: Peter Wise
4             # Module component: Parse::WebCounter
5             # ----------------------------------------------------------------------
6             # Module name: Parse::WebCounter
7             # Module state: First Release
8             # Module notes: Parses Image counters
9             #
10             # Module filename: Parse::WebCounter.pm
11             # ----------------------------------------------------------------------
12             # Version Author Date Comment
13             # ~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14             # 0.01 P.J.Wise 08/10/2004 Initial Version
15             # 0.02 P.J.Wise 18/12/2006 First Release to CPAN
16             #
17             # ----------------------------------------------------------------------
18             # CVS:
19             # ID: $Id: WebCounter.pm,v 1.13 2006/12/19 20:42:27 peter Exp $
20             #
21             # ----------------------------------------------------------------------
22             # Notes:
23             # ~~~~~~
24             # Module parses web counter images using GD and supplies the numeric
25             # value represented by the image. Useful if you have a cron keeping
26             # track of the number of hits you are getting per day and you don't
27             # have real logs to go by. You will need copies of the images
28             # representing the individual digits, or a strip of all of them for
29             # it to compare to as the module is not very bright it does a simple
30             # image comparison as apposed to any sophisticated image analysis
31             # (This is not designed, nor intended to be a Captcha solver).
32             # You will need to have GD compiled with support for the image format
33             # that your counters are displayed in. (Usually gif)
34             # ======================================================================
35             package Parse::WebCounter;
36              
37 3     3   135083 use 5.008;
  3         12  
  3         130  
38 3     3   116 use strict;
  3         8  
  3         112  
39 3     3   18 use warnings;
  3         8  
  3         98  
40              
41 3     3   6786 use GD;
  0            
  0            
42              
43             require Exporter;
44              
45             our @ISA = qw(Exporter);
46              
47             # Items to export into callers namespace by default. Note: do not export
48             # names by default without a very good reason. Use EXPORT_OK instead.
49             # Do not simply export all your public functions/methods/constants.
50              
51             # This allows declaration use Parse::WebCounter ':all';
52             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
53             # will save memory.
54             our %EXPORT_TAGS = ( 'all' => [ qw( readImage readDigit
55            
56             ) ] );
57              
58             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
59              
60             our @EXPORT = qw(
61            
62             );
63              
64              
65              
66             use vars qw($VERSION);
67              
68             $VERSION = '0.02';
69              
70              
71             #-------------------------------------------------------------------------------
72             # Parse::WebCounter::new()
73             #-------------------------------------------------------------------------------
74             # Purpose:
75             # Constructor
76             #-------------------------------------------------------------------------------
77             # Parameters:
78             # takes a Hash or hashref of module parameters recognises the following
79             # options
80             # Name Default Notes
81             # DIGITWIDTH 15 Width of individual digit
82             # DIGITHEIGHT 20 Height of individual digit
83             # STRIPORDER 1234567890 Order of digits in the image strip (if used)
84             # MODE STRIP Use image strip or "DIGITS"
85             # TYPE gif File type of images
86             # PATTERN a Pattern dir
87             # UNKOWNCHAR char Character to use if digit not matched
88             #
89             # (Image file loaded = PATTERN/STRIP.TYPE or PATTER/0.TYPE -> 9.TYPE)
90             # -----------------------------------------------------------------------------
91             # Returns:
92             # ObjectRef Self
93             #
94             #-------------------------------------------------------------------------------
95             sub new{
96             my $proto = shift;
97             my @args = @_;
98             my $class = ref($proto) || $proto;
99             my $self = {};
100             bless($self,$class);
101              
102             #Defaults...
103             $self->{DIGITWIDTH} = 15;
104             $self->{DIGITHEIGHT}= 20;
105             $self->{STRIPORDER} = "1234567890";
106             $self->{MODE} = "STRIP";
107             $self->{TYPE} = "gif";
108             $self->{PATTERN} = "a";
109             $self->{UNKNOWNCHAR}= "_";
110              
111             my $rprops;
112             if (ref($args[0]) eq 'HASH'){
113             my $rtgash = %{$args[0]};
114             $rprops = $self->_cap_hash($args[0]);
115             }else{
116             $rprops = $self->_cap_hash({ @args });
117             }
118             foreach my $k (qw(DIGITWIDTH DIGITHEIGHT STRIPORDER TYPE PATTERN UNKNOWNCHAR)){
119             if (exists($rprops->{$k})){
120             $self->{$k} = $rprops->{$k};
121             }
122             }
123             #need to be special with MODE
124             if (exists($rprops->{MODE})){
125             $rprops->{MODE} =~ tr/a-z/A-Z/;
126             if ($rprops->{MODE} ne "STRIP" && $rprops->{MODE} ne "DIGITS"){
127             warn "Invalid mode " . $rprops->{MODE} . " using default\n";
128             }else{
129             $self->{MODE} = $rprops->{MODE};
130             }
131             }
132              
133              
134             $self->_init();
135            
136             return $self;
137             }
138              
139             #-------------------------------------------------------------------------------
140             # Parse::WebCounter::_init()
141             #-------------------------------------------------------------------------------
142             # Purpose:
143             # Internal function to initialise the class data (loads image strip data)
144             #-------------------------------------------------------------------------------
145             # Parameters:
146             # None - pulls data from class object and $ENV
147             #
148             #-------------------------------------------------------------------------------
149             # Returns:
150             # Nothing
151             #
152             #-------------------------------------------------------------------------------
153             sub _init{
154             my $self = shift;
155              
156             if ($self->{MODE} eq "STRIP"){
157             $self->_loadStripImage();
158             }else{
159             $self->_loadDigitImages();
160             }
161             }
162              
163              
164              
165             #-------------------------------------------------------------------------------
166             # Parse::WebCounter::_cap_hash()
167             #-------------------------------------------------------------------------------
168             # Purpose:
169             # automagically convert the hash it is given into capitalised keys so users
170             # of the module can pass any capitalisation they like as module options
171             #-------------------------------------------------------------------------------
172             # Parameters:
173             # HashRef
174             #
175             #-------------------------------------------------------------------------------
176             # Returns:
177             # HashRef all the keys CAPITALISED
178             #
179             #-------------------------------------------------------------------------------
180             sub _cap_hash {
181             my $self = shift;
182             my $rhash = shift;
183             my %hash = map {
184             my $k = $_;
185             my $v = $rhash->{$k};
186             $k =~ tr/a-z/A-Z/;
187             $k => $v;
188             } keys(%{$rhash});
189             return \%hash;
190             }
191              
192             #-------------------------------------------------------------------------------
193             # Parse::WebCounter::_loadStripImage()
194             #-------------------------------------------------------------------------------
195             # Purpose:
196             # Loads the images required for matching from a single strip of digits in
197             # one image and breaks it up into individual ones.
198             #-------------------------------------------------------------------------------
199             # Parameters: None, but uses following class data
200             # digitwidth int The width of the digits in the strip
201             # digitheight int The height of the digits in the strip
202             # strip order string the "order" of the digits ie "1234567890"
203             # type string The "type" of image, essentially the extension
204             # pattern string the pattern directory to load from relative to current
205             #-------------------------------------------------------------------------------
206             # Returns:
207             # Nothing, But stores the image data in the class.
208             #
209             #-------------------------------------------------------------------------------
210             sub _loadStripImage{
211             my $self = shift;
212             my %reference_images;
213             my $filename = $self->{PATTERN} . "/strip." . $self->{TYPE};
214             my $imagestrip = GD::Image->new($filename);
215             my $left = 0;
216             my @striporder = split('',$self->{STRIPORDER});
217             foreach my $number (@striporder){
218             my $digit = GD::Image->new($self->{DIGITWIDTH},
219             $self->{DIGITHEIGHT});
220             $digit->copy($imagestrip,0,0,$left,0,$self->{DIGITWIDTH}
221             ,$self->{DIGITHEIGHT});
222             $left += $self->{DIGITWIDTH};
223             $reference_images{$number} = $digit;
224             }
225             $self->{REFIMAGES} = \%reference_images;
226             }
227              
228             #-------------------------------------------------------------------------------
229             # Parse::WebCounter::_loadDigitImages()
230             #-------------------------------------------------------------------------------
231             # Purpose:
232             # Loads the images required for matching from separate digit files
233             #
234             #-------------------------------------------------------------------------------
235             # Parameters: None, but uses following class data
236             # digitwidth int The width of the digits in the strip
237             # digitheight int The height of the digits in the strip
238             # strip order string the "order" of the digits ie "1234567890"
239             # type string The "type" of image, essentially the extension
240             # pattern string the pattern directory to load from relative to current
241             #
242             #-------------------------------------------------------------------------------
243             # Returns:
244             # Nothing, But stores the image data in the class.
245             #
246             #-------------------------------------------------------------------------------
247             sub _loadDigitImages{
248             my $self = shift;
249             my %reference_images;
250             my @striporder = split('',$self->{STRIPORDER});
251             foreach my $number (@striporder){
252             $reference_images{$number} = GD::Image->new( $self->{PATTERN} . "/" . $number . "." . $self->{TYPE});
253             }
254              
255             $self->{REFIMAGES} = \%reference_images;
256              
257             }
258              
259             #-------------------------------------------------------------------------------
260             # Parse::WebCounter::readImage(image[,xoffset[,yoffset]])
261             #-------------------------------------------------------------------------------
262             # Purpose:
263             # Reads the given image to determine the value of all the digits within
264             #
265             #-------------------------------------------------------------------------------
266             # Parameters:
267             # image gdimage The image object to evaluate
268             # xoffset int Offset value to use (in case image has a border)
269             # yoffset int Offset value
270             #
271             # Values from classdata used
272             # digitwidth int The width of the digits in the strip
273             # digitheight int The height of the digits in the strip
274             #
275             #-------------------------------------------------------------------------------
276             # Returns:
277             # The parsed value of the image.
278             #
279             #-------------------------------------------------------------------------------
280             sub readImage{
281             my $self = shift;
282             my $image = shift;
283             my $xoffset = shift || 0;
284             my $yoffset = shift || 0;
285             my ($width,$height) = $image->getBounds();
286             my $return = "";
287             for (my $i = $xoffset; $i < $width ; $i += $self->{DIGITWIDTH}){
288             my $digit = GD::Image->new($self->{DIGITWIDTH},
289             $self->{DIGITHEIGHT});
290             $digit->copy($image,0,0,$i,$yoffset,
291             $self->{DIGITWIDTH},
292             $self->{DIGITHEIGHT});
293             $return .= $self->readDigit($digit);
294             }
295             return $return;
296              
297             }
298              
299              
300             #-------------------------------------------------------------------------------
301             # Parse::WebCounter::readDigit(image)
302             #-------------------------------------------------------------------------------
303             # Purpose:
304             # Reads the given image digit to determine the value
305             #
306             #-------------------------------------------------------------------------------
307             # Parameters:
308             # image gdimage The image object to evaluate
309             #
310             # Values from classdata used
311             # REFIMAGES hashref The stored reference images for comparison
312             # UNKNOWNCHAR char The character to return for an unmatched digit ('_')
313             #
314             #-------------------------------------------------------------------------------
315             # Returns:
316             # The parsed value of the digit, or the UNKNOWNCHAR if the digit could not
317             # be matched.
318             #
319             #-------------------------------------------------------------------------------
320             sub readDigit{
321             my $self = shift;
322             my $image = shift;
323             foreach my $number (keys(%{$self->{REFIMAGES}})){
324             if ($image->compare($self->{REFIMAGES}->{$number}) == 0){
325             return $number;
326             }
327             }
328             return $self->{UNKNOWNCHAR};
329             }
330              
331             1;
332              
333             __END__