File Coverage

blib/lib/Plack/Middleware/Favicon.pm
Criterion Covered Total %
statement 21 53 39.6
branch 0 22 0.0
condition 0 5 0.0
subroutine 7 11 63.6
pod 2 2 100.0
total 30 93 32.2


line stmt bran cond sub pod time code
1             package Plack::Middleware::Favicon;
2 4     4   306335 use strict;
  4         10  
  4         172  
3 4     4   24 use warnings;
  4         8  
  4         167  
4 4     4   24 use Carp qw/croak/;
  4         9  
  4         311  
5 4     4   669 use parent 'Plack::Middleware';
  4         332  
  4         31  
6 4     4   39626 use Imager;
  4         31586  
  4         37  
7 4     4   3100 use HTTP::Date qw/time2str/;
  4         16895  
  4         338  
8 4         39 use Plack::Util::Accessor qw/
9             src_image_file
10             src_image_obj
11             cache
12             custom_favicons
13 4     4   35 /;
  4         6  
14              
15             our $VERSION = '0.04';
16              
17             our @DEFAULT_FAVICONS = map {
18             $_->{type} ||= 'png';
19             $_->{mime_type} ||= 'image/png';
20             $_;
21             } (
22             {
23             cond => sub {
24             my ($self, $env) = @_;
25             $env->{HTTP_USER_AGENT}
26             and $env->{HTTP_USER_AGENT} =~ m!MSIE (?:[1-9]|10)\.!;
27             },
28             path => qr!^/favicon\.ico!, size => [16, 16],
29             type => 'ico', mime_type => 'image/x-icon',
30             },
31             { path => qr!^/favicon\.ico!, size => [16, 16], mime_type => 'image/x-icon', },
32             { path => qr!^/favicon-16x16\.png!, size => [16, 16], },
33             { path => qr!^/favicon-32x32\.png!, size => [32, 32], },
34             { path => qr!^/favicon-96x96\.png!, size => [96, 96], },
35             { path => qr!^/favicon-160x160\.png!, size => [160, 160], },
36             { path => qr!^/favicon-196x196\.png!, size => [196, 196], },
37             { path => qr!^/mstile-70x70\.png!, size => [70, 70], },
38             { path => qr!^/mstile-144x144\.png!, size => [144, 144], },
39             { path => qr!^/mstile-150x150\.png!, size => [150, 150], },
40             { path => qr!^/mstile-310x310\.png!, size => [310, 310], },
41             { path => qr!^/mstile-310x150\.png!, size => [310, 150], },
42             { path => qr!^/apple-touch-icon-57x57\.png!, size => [57, 57], },
43             { path => qr!^/apple-touch-icon-60x60\.png!, size => [60, 60], },
44             { path => qr!^/apple-touch-icon-72x72\.png!, size => [72, 72], },
45             { path => qr!^/apple-touch-icon-76x76\.png!, size => [76, 76], },
46             { path => qr!^/apple-touch-icon-114x114\.png!, size => [114, 114], },
47             { path => qr!^/apple-touch-icon-120x120\.png!, size => [120, 120], },
48             { path => qr!^/apple-touch-icon-144x144\.png!, size => [144, 144], },
49             { path => qr!^/apple-touch-icon-152x152\.png!, size => [152, 152], },
50             { path => qr!^/apple-touch-icon\.png!, size => [57, 57], },
51             { path => qr!^/apple-touch-icon-precomposed\.png!, size => [57, 57], },
52             );
53              
54             sub prepare_app {
55 0     0 1   my $self = shift;
56              
57 0 0         croak "required 'src_image_file'"
58             unless $self->src_image_file;
59 0 0         croak "not found or not a file:". $self->src_image_file
60             unless -f $self->src_image_file;
61              
62 0           my $imager = Imager->new(file => $self->src_image_file);
63 0 0         unless ($imager) {
64 0   0       croak sprintf(
65             "could not create object from %s. %s",
66             $self->src_image_file,
67             Imager->errstr || '',
68             );
69             }
70 0           $self->src_image_obj($imager);
71              
72 0 0         if ($self->cache) {
73 0 0         for my $f (@{$self->custom_favicons || []}, @DEFAULT_FAVICONS) {
  0            
74 0           $self->_generate($f);
75             }
76             }
77             }
78              
79             sub call {
80 0     0 1   my ($self, $env) = @_;
81              
82 0 0         for my $f (@{$self->custom_favicons || []}, @DEFAULT_FAVICONS) {
  0            
83 0 0 0       next if $f->{cond} && !$f->{cond}->($self, $env);
84 0 0         if ($env->{PATH_INFO} =~ m!$f->{path}!) {
85 0           my $content = $self->_generate($f);
86             return [
87 0           200,
88             [
89             'Content-Type' => $f->{mime_type},
90             'Content-Length' => length $content,
91             'Last-Modified' => time2str(time),
92             ],
93             [$content],
94             ];
95             }
96             }
97              
98 0           $self->app->($env);
99             }
100              
101             sub _generate {
102 0     0     my ($self, $f) = @_;
103              
104 0 0         if ($self->cache) {
105 0 0         if ( my $cache = $self->cache->get($self->_ckey($f)) ) {
106 0           return $cache;
107             }
108             }
109              
110 0           my ($x, $y) = @{ $f->{size} };
  0            
111              
112 0           my $favicon = '';
113 0           $self->src_image_obj->scale(
114             xpixels => $x,
115             ypixels => $y,
116             type => 'nonprop',
117             )->write(
118             data => \$favicon,
119             type => $f->{type},
120             );
121              
122 0 0         if ($self->cache) {
123 0           $self->cache->set($self->_ckey($f) => $favicon);
124             }
125              
126 0           return $favicon;
127             }
128              
129 0     0     sub _ckey { join ':', @{$_[1]->{size}}, $_[1]->{type}; }
  0            
130              
131             1;
132              
133             __END__