File Coverage

blib/lib/Image/MetaData/GQview.pm
Criterion Covered Total %
statement 134 160 83.7
branch 34 66 51.5
condition 16 48 33.3
subroutine 17 17 100.0
pod 8 8 100.0
total 209 299 69.9


line stmt bran cond sub pod time code
1             package Image::MetaData::GQview;
2              
3 2     2   130359 use strict;
  2         11  
  2         54  
4              
5             #use warnings;
6             ## no critic (RequireUseWarnings);
7              
8 2     2   46 use 5.008000;
  2         7  
9 2     2   10 use Carp;
  2         4  
  2         109  
10 2     2   1273 use Fatal qw(:void open close);
  2         27629  
  2         8  
11 2     2   2532 use Cwd qw(abs_path);
  2         5  
  2         96  
12 2     2   12 use PerlIO;
  2         4  
  2         10  
13              
14             sub _abs_path;
15              
16             =head1 NAME
17              
18             Image::MetaData::GQview - Perl extension for GQview image metadata
19              
20             =head1 SYNOPSIS
21              
22             use Image::MetaData::GQview;
23              
24             my $md = Image::MetaData::GQview->new("test.jpg");
25             my $md2 = Image::MetaData::GQview->new("test2.jpg", {fields => ['keywords', 'comment', 'picture info']});
26             my $md3 = Image::MetaData::GQview->new({file => "test2.jpg", fields => ['keywords', 'comment', 'picture info']});
27             $md->load("test.jpg");
28             my $comment = $md->comment;
29             my @keywords = $md->keywords;
30             my $raw = $md->raw;
31             $md->comment("This is a comment");
32             $md->keywords(@keywords);
33             $md->save("test.jpg");
34              
35             =head1 DESCRIPTION
36              
37             This module is a abstraction to the image meta data of GQview.
38              
39             All internal errors will trow an error!
40              
41             =head2 METHODS
42              
43             =over
44              
45             =cut
46              
47 2     2   976 use version; our $VERSION = qv("v2.0.2");
  2         3643  
  2         12  
48              
49             =item new
50              
51             This is a class method and the only one. It is used to get a object of Image::MetaData::GQview. It can be called without parameter or with the image as only option in witch case it try to load the meta data.
52              
53             You can provide a hash reference as second or as only parameter which specify file and/or fields. The fields are default "keywords" and "comment" in this order.
54              
55             =cut
56              
57             sub new
58             {
59 2     2 1 79 my $param = shift;
60 2   33     12 my $class = ref($param) || $param;
61 2         4 my $file = shift;
62 2   50     7 my $opts = shift || {};
63              
64 2 100       7 if (ref($file) eq 'HASH')
65             {
66 1         2 $opts = $file;
67 1         2 $file = undef;
68             }
69              
70 2         7 my $self = {fields => [qw(keywords comment)],};
71 2 50       4 $self->{opts}->{file} = $file if $file;
72              
73 2         5 bless $self, $class;
74              
75 2         4 foreach (qw(file fields))
76             {
77 4 100       10 $self->{$_} = $opts->{$_} if exists($opts->{$_});
78             }
79              
80 2         8 $file = $self->{opts}->{file};
81              
82 2 50       4 $self->load($file) if $file;
83              
84 2         6 return $self;
85             } ## end sub new
86              
87             =item load
88              
89             If you didn't load the data with new you can do that with this method. If the parameter is left out the one setted before is used.
90              
91             You can also specify the location for the meta file as second parameter.
92              
93             =cut
94              
95             sub load
96             {
97 2     2 1 718 my $self = shift;
98 2   33     7 my $image = shift || $self->{imagefile};
99 2         4 my $metafile = shift;
100              
101 2 50       4 croak("No File given!") unless $image;
102 2         5 $image = _abs_path($image);
103 2 100 66     223 croak("No such file ($image)!") unless -e $image or -l $image;
104              
105 1         5 $self->{imagefile} = $image;
106              
107 1 50       3 unless ($metafile)
108             {
109 0         0 (my $metadata1 = $image) =~ s#/([^/]*)$#/.metadata/$1.meta#;
110 0         0 my $metadata2 = _abs_path($ENV{HOME}) . ".gqview/metadata$image.meta";
111              
112 0 0       0 $metafile = $metadata1 if -r $metadata1;
113 0 0 0     0 $metafile ||= $metadata2 if -r $metadata2;
114             } ## end unless ($metafile)
115 1         2 $self->{metafile} = $metafile;
116              
117 1 50       3 croak("No metadata found for image '$image'!") unless $metafile;
118              
119 1         24 open my $in, "<:utf8", $metafile;
120 1         55 $self->{metadata} = eval { local $/ = undef; <$in> };
  1         4  
  1         36  
121 1         25 close $in;
122              
123             # Aufbau:
124             # #GQview comment ()
125             #
126             # [keywords]
127             # ...
128             #
129             # [comment]
130             # ...
131             #
132             # #end
133 1         23 my $select = join("|", @{$self->{fields}});
  1         5  
134 1         42 my @fields_ext = split(/^\[($select)\]\n/m, $self->{metadata});
135              
136             # trow away the head
137 1         4 shift @fields_ext;
138 1 50       5 die "Internal Error: Metadata are not parsable" if (@fields_ext % 2) != 0; ## no critic (RequireCarping);
139              
140             # Cleanup the last field if it exists
141 1 50       22 $fields_ext[-1] =~ s/\n*#end\n?\z/\n/ if @fields_ext > 0;
142              
143             # Now they can be put into $self
144 1         7 my %fields = @fields_ext;
145 1         3 $self->{data} = \%fields;
146              
147 1         7 return 1;
148             } ## end sub load
149              
150             =item comment
151              
152             Get or set the comment.
153              
154             =cut
155              
156             sub comment
157             {
158 2     2 1 1874 my $self = shift;
159 2         16 my $comment = shift;
160              
161 2 100       9 $comment =~ s/^\[/ [/mg if $comment;
162 2 100       8 $self->set_field('comment', $comment) if $comment;
163              
164 2         4 return scalar($self->get_field('comment'));
165             } ## end sub comment
166              
167             =item keywords
168              
169             Get or set the keywords. This is the preferred method for the keywords as it shift out empty keywords.
170              
171             =cut
172              
173             sub keywords ## no critic (RequireArgUnpacking);
174             {
175 2     2 1 347 my $self = shift;
176              
177 2 100       8 $self->set_field('keywords', @_) if @_;
178              
179 2         6 my @keywords = grep {$_} $self->get_field('keywords');
  4         13  
180              
181 2         10 return @keywords;
182             } ## end sub keywords
183              
184             =item raw
185              
186             Get the raw data
187              
188             =cut
189              
190             sub raw
191             {
192 2     2 1 5 my $self = shift;
193              
194 2         21 return $self->{metadata};
195             }
196              
197             =item save
198              
199             Save the data to disk. This will read the location from the gqview configuration. If there is none, the info will be saved in local directory.
200              
201             You can also specify the location for the meta file as second parameter.
202              
203             =cut
204              
205             sub save
206             {
207 1     1 1 309 my $self = shift;
208 1         2 my $image = shift;
209 1         2 my $newimage = $image;
210 1         2 my $metafile = shift;
211 1         10 my $newmetafile = $metafile;
212 1   33     5 $image ||= $self->{imagefile};
213 1   33     2 $metafile ||= $self->{metafile};
214              
215 1 50       3 croak("No File given!") unless $image;
216 1         2 $image = _abs_path($image);
217 1 50 33     19 croak("No such file ($image)!") unless -e $image or -l $image;
218              
219 1         16 (my $metadata1 = $image) =~ s#/([^/]*)$#/.metadata/$1.meta#;
220 1         3 my $metadata2 = _abs_path($ENV{HOME}) . "/.gqview/metadata$image.meta";
221              
222 1         3 my $metadata;
223              
224             # Read the gqviewrc
225 1 50       35 if (open my $in, "<", $ENV{HOME} . "/.gqview/gqviewrc") ## no critic (RequireBriefOpen);
226             {
227 0         0 while (my $line = <$in>)
228             {
229 0         0 chomp $line;
230 0 0       0 next if $line =~ /^#/;
231 0 0       0 if ($line =~ /^local_metadata: (true|false)$/)
232             {
233 0 0       0 $metadata = ($1 eq "true") ? $metadata1 : $metadata2;
234 0         0 last;
235             }
236             } ## end while (my $line = <$in>)
237 0         0 close $in;
238             } ## end if (open my $in, "<", ...
239 1 50 33     82 if ($newimage and not $newmetafile)
240             {
241 0         0 $metafile = $metadata;
242             }
243              
244 1         2 my $false;
245 1         4 my @metadirs = split(/\//, $metafile);
246 1         2 pop @metadirs;
247 1         2 my $metadir = "";
248 1         3 while (@metadirs)
249             {
250 0         0 $metadir .= shift(@metadirs) . "/";
251 0 0 0     0 unless (-d $metadir or mkdir($metadir))
252             {
253 0         0 $false = 1;
254 0         0 last;
255             }
256             } ## end while (@metadirs)
257 1 0 33     3 if ($false and not $newmetafile and $metafile ne $metadata2)
      33        
258             {
259 0         0 $false = 0;
260 0         0 $metafile = $metadata2;
261 0         0 @metadirs = split(/\//, $metadata2);
262 0         0 pop @metadirs;
263 0         0 $metadir = "";
264 0         0 while (@metadirs)
265             {
266 0         0 $metadir .= shift(@metadirs) . "/";
267 0 0 0     0 unless (-d $metadir or mkdir($metadir))
268             {
269 0         0 $false = 1;
270 0         0 last;
271             }
272             } ## end while (@metadirs)
273             } ## end if ($false and not $newmetafile...
274 1 50       2 croak("Cannot create directory structure for meta file '$metafile'!") if ($false);
275 1         4 $self->_sync;
276 1 50       3 if ($self->raw)
277             {
278 1         32 open my $meta, ">:utf8", $metafile;
279 1 50       88 print $meta $self->raw or die("Faulty metadata"); ## no critic (RequireCarping);
280 1         24 close $meta;
281             } ## end if ($self->raw)
282              
283 1         55 $self->{imagefile} = $image;
284 1         3 $self->{metafile} = $metafile;
285              
286 1         7 return 1;
287             } ## end sub save
288              
289             =item get_field
290              
291             This will extract the information of one field and return it as single sting (in scalar context) or as array splitted in lines.
292              
293             Please note, it array context also empty lines can be returned!
294              
295             =cut
296              
297             sub get_field
298             {
299 4     4 1 7 my $self = shift;
300 4   33     9 my $field = shift || croak("get_field has to be called with a field as the first parameter");
301              
302 4 50       5 croak("get_field has to be called with a known field '$field' as first parameter") unless grep {/^\Q$field\E$/s} @{$self->{fields}};
  10         96  
  4         9  
303              
304 4   50     16 my $data = $self->{data}->{$field} || "";
305 4         26 $data =~ s/\n*\z//;
306              
307 4 100       27 return wantarray ? split(/\n/, $data) : "$data\n";
308             } ## end sub get_field
309              
310             =item set_field
311              
312             Well, of cause if you can get a field you have to be able to set it.
313              
314             The arguments are the field name and the data.
315              
316             The data can be a single value or a array.
317              
318             =cut
319              
320             sub set_field ## no critic (RequireArgUnpacking);
321             {
322 2     2 1 4 my $self = shift;
323 2   33     5 my $field = shift || croak("set_field has to be called with a field as the first parameter");
324              
325 2 50       3 croak("set_field has to be called with a known field '$field' }as first parameter") unless grep {/^\Q$field\E$/s} @{$self->{fields}};
  4         44  
  2         6  
326              
327 2         7 my $data = join("\n", @_);
328 2         11 $data =~ s/\n*\z/\n/;
329              
330 2         7 $self->{data}->{$field} = $data;
331              
332 2         6 $self->_sync;
333              
334 2         3 return 1;
335             } ## end sub set_field
336              
337             #
338             # Internal method _sync
339             #
340             # This will hold the metadata in sync with the single elements
341             #
342              
343             sub _sync
344             {
345 3     3   4 my $self = shift;
346              
347 3         7 $self->{metadata} = "#GQview comment (2.0.0)\n\n";
348              
349 3         5 foreach my $field (@{$self->{fields}})
  3         7  
350             {
351 6   100     17 my $data = $self->{data}->{$field} || "";
352 6         30 $data =~ s/\n*\z/\n\n/s;
353 6 100       16 $data = "\n" if $data eq "\n\n";
354 6         16 $self->{metadata} .= "[$field]\n" . $data;
355             } ## end foreach my $field (@{$self->...
356              
357 3         7 $self->{metadata} .= "#end\n";
358              
359 3         4 return 1;
360             } ## end sub _sync
361              
362             sub _abs_path
363             {
364 4     4   9 my $path = shift;
365              
366 4 100       19 $path = './' . $path unless $path =~ m#/#;
367 4         28 my ($p, $f) = $path =~ /^(.*)\/([^\/]*)$/;
368 4         46 $p = abs_path($p);
369              
370 4         36 return "$p/$f";
371             }
372              
373             1;
374              
375             __END__