File Coverage

blib/lib/Image/MetaData/GQview.pm
Criterion Covered Total %
statement 130 156 83.3
branch 32 64 50.0
condition 13 42 30.9
subroutine 16 16 100.0
pod 8 8 100.0
total 199 286 69.5


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