#!/usr/bin/perl # UCW Gallery: Generate published photos # (c) 2004--2015 Martin Mares use strict; use warnings; use UCW::Gallery; use Image::EXIF; use Image::Magick; use IO::Handle; use File::Spec; use File::Path; STDOUT->autoflush(1); my $gal = UCW::Gallery->load_config; my $orig_list = $gal->read_list('gallery.list') or die "Cannot read gallery.list: $!\n"; my $photo_dir = $gal->get('PhotoDir'); if (-d $photo_dir) { print "Using existing output directory: $photo_dir\n"; } else { print "Creating output directory: $photo_dir\n"; File::Path::mkpath($photo_dir) or die "Unable to create $photo_dir: $!\n"; } my $photo_meta = $gal->photo_meta_name; my $old_meta = {}; if (-f $photo_meta) { print "Reading old meta-data\n"; $old_meta = $gal->read_meta($photo_meta); # use Data::Dumper; print "Read old meta: ", Dumper($old_meta), "\n"; } my $meta = { 'photo' => {} }; sub get_meta_basic($$$) { my ($f, $m, $p) = @_; my $rotate = $f->{orientation}; my ($orig_w, $orig_h, $orig_size, $orig_format) = $p->PingImage($f->{orig}) or die "Error reading " . $f->{orig} . "\n"; print "${orig_w}x${orig_h} "; my ($w0, $h0) = ($rotate eq "l" || $rotate eq "r") ? ($orig_h, $orig_w) : ($orig_w, $orig_h); my ($w, $h) = ($w0, $h0); if ($w > $gal->get('PhotoMaxWidth')) { my $s = $gal->get('PhotoMaxWidth') / $w; $w = $w * $s; $h = $h * $s; } if ($h > $gal->get('PhotoMaxHeight')) { my $s = $gal->get('PhotoMaxHeight') / $h; $w = $w * $s; $h = $h * $s; } $w = int($w + .5); $h = int($h + .5); $m->{o} = $rotate; for my $k (keys %UCW::Gallery::list_attrs) { next if $UCW::Gallery::list_attrs{$k} < 2; $m->{$k} = $f->{$k} if defined $f->{$k}; } $m->{w0} = $w0; $m->{h0} = $h0; $m->{w} = $w; $m->{h} = $h; } sub parse_geo($) { my ($g) = @_; defined $g or return; if ($g =~ m{^([NEWS]) (\d+)\xb0 ([0-9.]+)'$}) { $g = $2 + $3/60; $g = -$g if $1 eq 'W' || $1 eq 'S'; } elsif ($g =~ m{^([NEWS]) (\d+)\xb0 (\d+)' ([0-9.]+)$}) { $g = $2 + $3/60 + $4/3600; $g = -$g if $1 eq 'W' || $1 eq 'S'; } else { print "[EXIF: unable to parse coordinate $g] "; return; } return sprintf "%.6f", $g; } sub get_meta_exif($$) { my ($f, $m) = @_; $gal->get('CacheExif') or return; my $e = new Image::EXIF($f->{orig}); my $i = $e->get_all_info(); if ($e->error) { print "[EXIF error: ", $e->error, "] "; return; } # use Data::Dumper; print Dumper($i); my $lat = parse_geo($i->{image}->{'Latitude'}); my $lon = parse_geo($i->{image}->{'Longitude'}); my $alt = $i->{image}->{'Altitude'}; if ($alt) { if ($alt =~ m{^([0-9.]+) m$}) { $alt = $1; } else { print "[EXIF: unable to parse altitude $alt] "; $alt = undef; } } # printf "[GEO: lat=%s lon=%s alt=%s] ", $lat // '?', $lon // '?', $alt // '?'; if ($lat && $lon) { $m->{lat} //= $lat; $m->{lon} //= $lon; } $m->{alt} //= $alt if $alt; my $time = $i->{image}->{'Image Created'}; if ($time) { if ($time =~ m{^(\d{4}):(\d{2}):(\d{2}) (\d{2}:\d{2}:\d{2})$}) { $m->{t} //= "$1-$2-$3 $4"; # print "[TIME: ", $m->{t}, "] "; } else { print "[EXIF: unable to parse time $time] "; } } } sub generate_photo($$$) { my ($f, $m, $p) = @_; my $e; $e = $p->Read($f->{orig}) and die "Error reading " . $f->{orig} . ": $e"; $p->Strip; $p->SetAttribute(quality=>90); my $xfrm = $m->{xf}; if ($xfrm =~ /s/) { print "-> sharpen "; $p->Sharpen(1); } if ($xfrm =~ /h/) { print "-> equalize "; $p->Equalize(); } if ($xfrm =~ /n/) { print "-> normalize "; $p->Normalize(); } my $rotate = $m->{o}; my $rot = 0; if ($rotate eq "l") { $rot = 270; } elsif ($rotate eq "r") { $rot = 90; } elsif ($rotate eq "u") { $rot = 180; } if ($rot) { print "-> rotate $rot "; $p->Rotate(degrees=>$rot); } my ($w, $h) = ($m->{w}, $m->{h}); if ($w != $m->{w0} || $h != $m->{h0}) { print "-> ${w}x${h} "; $p->Resize(width=>$w, height=>$h); } my $photo = $gal->photo_name($m, $f->{id}); my $tmp = "$photo.new"; $e = $p->Write($tmp) and die "Unable to write $tmp: $e"; rename $tmp, $photo or die "Cannot rename $tmp to $photo: $!\n"; } for my $f (@$orig_list) { my $id = $f->{id}; print "$id: "; my $m = { }; $meta->{photo}->{$id} = $m; $f->{orig} = File::Spec->rel2abs($f->{file}, $gal->get('OrigDir')); my $p = new Image::Magick; get_meta_basic($f, $m, $p); get_meta_exif($f, $m); my $om = $old_meta->{photo}->{$id}; if ($om && $om->{o} eq $m->{o} && $om->{xf} eq $m->{xf} && $om->{w} eq $m->{w} && $om->{h} eq $m->{h}) { print "... uptodate\n"; next; } generate_photo($f, $m, $p); print "... OK\n"; } print "Cleaning up stale files\n"; for my $f (<$photo_dir/*.jpg>) { my ($vv, $dd, $id) = File::Spec->splitpath($f); $id =~ s{\..*$}{}; unless (defined $meta->{photo}->{$id}) { print "$id: removing\n"; unlink $f; } } print "Writing meta-data\n"; $gal->write_meta($photo_meta, $meta); exit 0;