File Coverage

blib/lib/Plack/Middleware/Favicon.pm
Criterion Covered Total %
statement 27 53 50.9
branch 3 22 13.6
condition 1 5 20.0
subroutine 8 11 72.7
pod 2 2 100.0
total 41 93 44.0


line stmt bran cond sub pod time code
1             package Plack::Middleware::Favicon;
2 4     4   258222 use strict;
  4         9  
  4         149  
3 4     4   17 use warnings;
  4         5  
  4         122  
4 4     4   16 use Carp qw/croak/;
  4         7  
  4         204  
5 4     4   461 use parent 'Plack::Middleware';
  4         267  
  4         27  
6 4     4   31171 use Imager;
  4         32741  
  4         29  
7 4     4   2560 use HTTP::Date qw/time2str/;
  4         12651  
  4         321  
8 4         29 use Plack::Util::Accessor qw/
9             src_image_file
10             src_image_obj
11             cache
12             custom_favicons
13 4     4   27 /;
  4         7  
14              
15             our $VERSION = '0.03';
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 3     3 1 3386 my $self = shift;
56              
57 3 50       15 croak "required 'src_image_file'"
58             unless $self->src_image_file;
59 3 50       350 croak "not found or not a file:". $self->src_image_file
60             unless -f $self->src_image_file;
61              
62 3         135 my $imager = Imager->new(file => $self->src_image_file);
63 3 50       6137 unless ($imager) {
64 3   50     13 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__