File Coverage

blib/lib/App/Dthumb.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package App::Dthumb;
2              
3              
4             =head1 NAME
5              
6             App::Dthumb - Generate thumbnail index for a set of images
7              
8             =head1 SYNOPSIS
9              
10             use App::Dthumb;
11             use Getopt::Long qw(:config no_ignore_case);
12            
13             my $opt = {};
14            
15             GetOptions(
16             $opt,
17             qw{
18             help|h
19             size|d=i
20             spacing|s=f
21             no-lightbox|L
22             no-names|n
23             quality|q=i
24             version|v
25             },
26             );
27            
28             my $dthumb = App::Dthumb->new($opt);
29             $dthumb->run();
30              
31             =head1 VERSION
32              
33             This manual documents App::Dthumb version 0.2
34              
35             =cut
36              
37              
38 2     2   16121 use strict;
  2         14  
  2         173  
39 2     2   35 use warnings;
  2         5  
  2         148  
40 2     2   1784 use autodie;
  2         66623  
  2         25  
41 2     2   14597 use 5.010;
  2         19  
  2         107  
42              
43 2     2   12 use base 'Exporter';
  2         4  
  2         239  
44              
45 2     2   5089 use App::Dthumb::Data;
  2         7  
  2         115  
46 2     2   17 use Cwd;
  2         4  
  2         152  
47 2     2   898 use Image::Imlib2;
  0            
  0            
48              
49             our @EXPORT_OK = ();
50             our $VERSION = '0.2';
51              
52              
53             =head1 METHODS
54              
55             =head2 new($conf)
56              
57             Returns a new B object. As you can see in the SYNOPSIS, $conf is
58             designed so that it can be directly fed by B.
59              
60             Valid hash keys are:
61              
62             =over
63              
64             =item B => I
65              
66             Set base directory for image reading, data creation etc.
67              
68             Default: F<.> (current working directory)
69              
70             =item B => I
71              
72             Set name of the html index file
73              
74             Default: F
75              
76             =item B => I
77              
78             Include and use javascript lightbox code
79              
80             Default: true
81              
82             =item B => I
83              
84             If true, unconditionally recreate all thumbnails.
85              
86             Default: false
87              
88             =item B => I
89              
90             Maximum image size in pixels, either width or height (depending on image
91             orientation)
92              
93             Default: 200
94              
95             =item B => I
96              
97             Spacing between image boxes. 1.0 means each box is exactly as wide as the
98             maximum image width (see B), 1.1 means slightly larger, et cetera
99              
100             Default: 1.1
101              
102             =item B => I
103              
104             Show image name below thumbnail
105              
106             Default: true
107              
108             =item B => I<0 .. 100>
109              
110             Thumbnail image quality
111              
112             Default: 75
113              
114             =back
115              
116             =cut
117              
118              
119             sub new {
120             my ($obj, %conf) = @_;
121             my $ref = {};
122              
123             $conf{quality} //= 75;
124             $conf{recreate} //= 0;
125             $conf{size} //= 200;
126             $conf{spacing} //= 1.1;
127             $conf{title} //= (split(qr{/}, cwd()))[-1];
128              
129             $conf{file_index} //= 'index.xhtml';
130             $conf{dir_images} //= '.';
131              
132             $conf{dir_data} = "$conf{dir_images}/.dthumb";
133             $conf{dir_thumbs} = "$conf{dir_images}/.thumbs";
134              
135             # helpers to directly pass GetOptions results
136             $conf{lightbox} //= ( $conf{'no-lightbox'} ? 0 : 1 );
137             $conf{names} //= ( $conf{'no-names'} ? 0 : 1 );
138              
139             $ref->{config} = \%conf;
140              
141             $ref->{data} = App::Dthumb::Data->new();
142              
143             $ref->{data}->set_vars(
144             title => $conf{title},
145             width => $conf{size} * $conf{spacing} . 'px',
146             height => $conf{size} * $conf{spacing} . 'px',
147             );
148              
149             $ref->{html} = $ref->{data}->get('html_start.dthumb');
150              
151             return bless($ref, $obj);
152             }
153              
154             =head2 read_directories
155              
156             Read in a list of all image files in the current directory and all files in
157             F<.thumbs> which do not have a corresponding full-size image.
158              
159             =cut
160              
161              
162             sub read_directories {
163             my ($self) = @_;
164             my $thumbdir = $self->{config}->{dir_thumbs};
165             my $imgdir = $self->{config}->{dir_images};
166             my $dh;
167             my (@files, @old_thumbs);
168              
169             opendir($dh, $imgdir);
170              
171             for my $file (readdir($dh)) {
172             if (-f "${imgdir}/${file}" and $file =~ qr{ \. (png | jp e? g) $ }iox) {
173             push(@files, $file);
174             }
175             }
176             closedir($dh);
177              
178             if (-d $thumbdir) {
179             opendir($dh, $thumbdir);
180             for my $file (readdir($dh)) {
181             if ($file =~ qr{^ [^.] }ox and not -f "${imgdir}/${file}") {
182             push(@old_thumbs, $file);
183             }
184             }
185             closedir($dh);
186             }
187              
188             @{$self->{files}} = sort { lc($a) cmp lc($b) } @files;
189             @{$self->{old_thumbnails}} = @old_thumbs;
190             }
191              
192              
193             =head2 create_files
194              
195             Makes sure the F<.thumbs> directory exists.
196              
197             Also, if lightbox is enabled (which is the default), creates the F<.dthumb>
198             directory and fills it with all required files.
199              
200             =cut
201              
202              
203             sub create_files {
204             my ($self) = @_;
205             my $thumbdir = $self->{config}->{dir_thumbs};
206             my $datadir = $self->{config}->{dir_data};
207              
208             if (not -d $thumbdir) {
209             mkdir($thumbdir);
210             }
211              
212             if ($self->{config}->{lightbox}) {
213              
214             if (not -d $datadir) {
215             mkdir($datadir);
216             }
217              
218             for my $file ($self->{data}->list_archived()) {
219             open(my $fh, '>', "${datadir}/${file}");
220             print {$fh} $self->{data}->get($file);
221             close($fh);
222             }
223             }
224             }
225              
226              
227             =head2 delete_old_thumbnails
228              
229             Unlink all no longer required thumbnails (as previously found by
230             B).
231              
232             =cut
233              
234              
235             sub delete_old_thumbnails {
236             my ($self) = @_;
237             my $thumbdir = $self->{config}->{dir_thumbs};
238              
239             for my $file (@{$self->{old_thumbnails}}) {
240             unlink("${thumbdir}/${file}");
241             }
242             }
243              
244              
245             =head2 get_files
246              
247             Returns an array of all image files found by B.
248              
249             =cut
250              
251              
252             sub get_files {
253             my ($self) = @_;
254              
255             return @{$self->{files}};
256             }
257              
258              
259             =head2 create_thumbnail_html($file)
260              
261             Append the necessary lines for $file to the HTML.
262              
263             =cut
264              
265              
266             sub create_thumbnail_html {
267             my ($self, $file) = @_;
268             my $div_width = $self->{config}->{size} * $self->{config}->{spacing};
269             my $div_height = $div_width + ($self->{config}->{names} ? 10 : 0);
270              
271             $self->{html} .= "
\n";
272              
273             $self->{html} .= sprintf(
274             "\t\n"
275             . "\t\t\"%s\"\n",
276             ($file) x 2,
277             $self->{config}->{dir_thumbs},
278             ($file) x 2,
279             );
280              
281             if ($self->{config}->{names}) {
282             $self->{html} .= sprintf(
283             "\t
\n"
284             . "\t%s\n",
285             'text-decoration: none',
286             ($file) x 2,
287             );
288             }
289              
290             $self->{html} .= "\n";
291             }
292              
293              
294             =head2 create_thumbnail_image($file)
295              
296             Load F<$file> and save a resized version in F<.thumbs/$file>. Skips thumbnail
297             generation if the thumbnail already exists and has a more recent mtime than
298             the original file.
299              
300             =cut
301              
302              
303             sub create_thumbnail_image {
304             my ($self, $file) = @_;
305             my $thumbdir = $self->{config}->{dir_thumbs};
306             my $thumb_dim = $self->{config}->{size};
307              
308             if (
309             -e "${thumbdir}/${file}"
310             and not $self->{config}->{recreate}
311             and (stat($file))[9] <= (stat("${thumbdir}/${file}"))[9]
312             ) {
313             return;
314             }
315              
316             my $image = Image::Imlib2->load($file);
317             my ($dx, $dy) = ($image->width(), $image->height());
318             my $thumb = $image;
319              
320             if ($dx > $thumb_dim or $dy > $thumb_dim) {
321             if ($dx > $dy) {
322             $thumb = $image->create_scaled_image($thumb_dim, 0);
323             }
324             else {
325             $thumb = $image->create_scaled_image(0, $thumb_dim);
326             }
327             }
328              
329             $thumb->set_quality($self->{config}->{quality});
330             $thumb->save("${thumbdir}/${file}");
331             }
332              
333              
334             =head2 write_out_html
335              
336             Write the cached HTML data to F.
337              
338             =cut
339              
340              
341             sub write_out_html {
342             my ($self) = @_;
343              
344             $self->{html} .= $self->{data}->get('html_end.dthumb');
345              
346             open(my $fh, '>', $self->{config}->{file_index});
347             print {$fh} $self->{html};
348             close($fh);
349             }
350              
351             sub version {
352             return $VERSION;
353             }
354              
355             1;
356              
357             __END__