#!/usr/bin/perl -w use strict; # pagemunger, a script to automate image gallery maintenance # $Id: pagemunger-wry-1.11,v 1.1 2004/03/02 22:46:34 wry Exp $ # # Copyright (C) 2003 Anthony de Boer # Copyright (C) 2003 W. Ross Younger # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ### # Don't read me! # # Information, ramblings and a poor excuse for documentation are at # the bottom of the script. Don't run this script randomly unless you don't # mind the current working directory (and any subdirectories of it) suddenly # sprouting image thumbnails and HTML pages. ### my %jt; my %pt; my $DB = '.data'; my %db; my %pgr; my $thumbprefix = '.t_'; my %hascap; my $doctype = ' '; if (-f $DB) { open(DB, $DB) || die "Cannot read $DB: $!\n"; $/ = ''; while() { my ($pg, $id, $rest) = split(/\s+/, $_, 3); next if $pg eq 'unsorted' && $id =~ /^\d+$/; $rest =~ s/\n+$//; die "DB entry $pg $id defined again!\n" if $db{"$pg $id"}; $db{"$pg $id"} = $rest; $hascap{$pg} = 1 if $id =~ /^\d+$/ && $rest =~ /\s\S/; } close(DB); } undef $/; sub do_dir { my $thedir = shift; my @subdirs = (); print STDERR "Entering `$thedir'...\n"; opendir(D, $thedir) || die "Cannot opendir .: $!\n"; for my $f (readdir(D)) { next unless defined($f); my $ff = $thedir."/".$f; if ($ff =~ /\.html$/) { next if $ff =~ /^unsorted/; open(F, $ff) || die "Cannot read $ff: $!\n"; $_ = ; $pt{$ff} = $_; while(s/$pt<\/A>\n"; } delete ($pgr{'unsorted'}); print STDERR "=== Output phase:\n"; for my $pg (sort (keys %pgr)) { &setgen($pg); } my $u = 0; $pgr{'unsorted'} = ''; for my $i (sort (keys %jt)) { next if $jt{$i} & 10; print STDERR "$i not in any page\n"; if ($i =~ /^img_(\d+)\.jpg$/ && $1 > 0) { $u = $1 + 0; } else { $u++; } while($db{"unsorted $u"}) { $u++; } $pgr{'unsorted'} .= "$u "; $db{"unsorted $u"} = $i; } open(DB, ">$DB.new") || die "Cannot write $DB.new: $!\n"; for my $k (sort (keys %db)) { die "Error writing $DB.new: $!\n" unless print DB "$k $db{$k}\n\n"; } close(DB) || die "Error closing $DB.new: $!\n"; rename ($DB, "$DB.bak"); rename ("$DB.new", $DB) || die "Cannot rename $DB.new to $DB: $!\n"; &setgen('unsorted') if $u || $db{"unsorted title"}; sub makealt # takes imagefile, comment { $_=shift; # We really only want base filename. # It sucks as an alt tag, but hey, this is a photo gallery. s,.*/($thumbprefix)?,,; return "ALT=\"$_\""; } sub setgen { my ($pg) = @_; my $title = $db{"$pg title"} || "foo"; my $headextra = $db{"$pg indexhead"} || ""; my @subs = sort({$a <=> $b} (split(/\s+/, $pgr{$pg}))); my $ttl = $#subs + 1; my $main = &header($title, 0, $ttl, $doctype, $headextra); my $mnbar = &navbar($pg, 0, $ttl); $main .= $mnbar . "\n"; $main .= "

".$db{"$pg intro"}."

\n\n" if $db{"$pg intro"}; print STDERR "ixl undefined\n" unless defined($ixl); if($pg eq 'index' && $db{'index auto'}) { $main .= "
    \n$ixl<\/UL>\n" } else { if ($hascap{$pg}) { $main .= "\n" } else { $main .= "

    \n" } } for my $i (1..$ttl) { my $snb = &navbar($pg, $i, $ttl); my ($im, $tx) = split(/\s+/, $db{"$pg $subs[$i-1]"}, 2); $tx = ' ' unless $tx; my $photo; my $alt = makealt($im); my $thumbalt = $alt; $thumbalt=~s/"$/ (thumbnail)"/; $jt{$im} |= 8; print STDERR "$im referenced but not found\n" unless $jt{$im} & 1; print STDERR addthumbprefix($im)." referenced but not found\n" unless $jt{$im} & 4; $photo = "

    \n"; } else { $main .= "\n"; } my $headextra = $db{"$pg pichead"} || ""; my $spg = &header($title, $i, $ttl, $doctype, $headextra); $spg .= "
    $photo
    \n"; $spg .= "
    $tx
    \n"; my $ptail = $db{"$pg tail"} || ""; $ptail = "
    ".$ptail."
    " if ($ptail ne ""); $spg .= "
    \n" . $snb . "\n" . $ptail. &footer; &flush("$pg\-$i.html", $spg); } $main .= "
    $photo
    $tx
    \n" unless (($pg eq 'index' && $db{'index auto'}) || !$hascap{$pg}); $main .= "
    $mnbar\n"; my $tail = $db{"$pg tail"} || ""; $tail = "
    $tail
    " if ($tail ne ""); $main .= $tail."\n\n".&footer; &flush("$pg.html", $main); } sub header { my ($title, $inof, $ttl, $doctype, $headextra) = @_; $headextra = "" unless defined $headextra; $doctype = "" unless defined $doctype; my $tcit = $inof ? "- $inof of $ttl" : ''; my $hs = $inof ? 'H3' : 'H1'; return "$doctype\n\n$title$tcit<\/TITLE>\n$headextra<\/HEAD>\n\n<BODY>\n<$hs CLASS=\"pagehead\">$title$tcit<\/$hs>\n"; } sub navbar { my ($pg, $inof, $ttl) = @_; my $parent = './'; my $r = ''; $parent = '../' if $pg eq 'index' && !$inof; $parent = "$pg.html" if $pg ne 'index' && $inof; $r .= "<P CLASS=\"navbar\"><A HREF=\"$parent\">[up]<\/A> "; $r .= "<A HREF=\"$pg\-".($inof-1).".html\">[previous]<\/A> " if $inof>1; $r .= "<A HREF=\"$pg\-".($inof+1).".html\">[next]<\/A> " if $inof && $inof<$ttl; $r .= " -- "; if ($hascap{$pg}) { for my $i (1..$ttl) { if ($i == $inof) { $r .= "<B>$i<\/B> "; } else { $r .= "<A HREF=\"$pg\-$i.html\">$i<\/A> "; } } } $r =~ s/ -- $//; $r .= "</P>\n"; return $r; } sub footer { return "\n<!-- generated -->\n<\/BODY>\n<\/HTML>\n"; } sub flush { my ($fn, $content) = @_; my $ocon = ''; if (defined($pt{$fn})) { my $pold = delete($pt{$fn}); if ($pold eq $content) { # print STDERR "No change to $fn.\n"; return; } elsif ($pold !~ /\<\!\-\- generated \-\-\>/) { print STDERR "Flagged against overwriting $fn\n"; return; } } open(F, ">$fn.new") || die "Cannot write $fn.new: $!"; die "Cannot print to $fn.new: $!" unless print F $content; close(F) || die "Error closing $fn.new: $!"; my $newer = 1; if (-f $fn) { # file exists, see if it has changed. my $rv = system "cmp $fn $fn.new >/dev/null"; die "couldn't cmp $fn $fn.new: $!" if ($rv<0); die "cmp exploded: $rv" if ($rv>0 && $rv<256); # get the actual exit code - zero if unchanged $rv>>=8; $newer=0 if ($rv==0); } if ($newer) { rename "$fn.new","$fn" or die "renaming $fn: $!"; print STDERR "> Wrote new $fn\n"; } else { print STDERR "$fn: unchanged\n"; unlink "$fn.new" or die "unlinking tempfile $fn.new: $!"; } } __END__ ######################################################################## Here are the promised ramblings pretending to be documentation, some of which is cribbed and paraphrased from adb's readme. I found that the pagemunger script in adb's pagemunger package[0] did something approximating what I wanted to do with regards to automatically generating thumbnails and picture index pages. I hacked it to suit, and pagemunger-wry was born. [0] http://www.leftmind.net/projects/pagemunger/ There are a few assumptions: * No need for CGIs, PHP or any other dynamic page mechanism. This is all static HTML and images. * Per-picture captions are optional. * No attempt is made to interact with access control; if you want any, do it yourself. * The output should pass validator.w3.org and be amenable to CSS, if desired. pagemunger-wry takes no arguments. It recursively scans, beginning with the current directory, for files of extensions jpg/jpeg. Any image without a thumbnail (prefixed by `.t_', though this is set globally in $thumbprefix) has one created for it, 150pixels wide. The script creates a `.data' file in the working directory if one does not exist; any new or otherwise unreferenced images are added to the `unsorted' section in arbitrary order. Once the scan is complete, `.data' is used to build HTML pages with linked and embedded images, with optional captions. Typically you'll want to run the script once to pick up the images, edit .data to suit, then rerun the script and examine the results. If desired, rinse and repeat. The .data file is paragraph-parsed. An empty line separates entries, and whitespace separates tokens in an entry. Typical entries include: pageid seqno imagename.jpeg {optional caption text} This places the named image on page `pageid' at position `seqno'. Pageid is an alphanumeric identifier, one per set of pictures; each distinct pageid gets its own thumbnails page, pageid.html. Seqno is a sequence number, integer, and they don't have to be consecutive. Think BASIC-program line numbers. Caption text is optional; if a caption is present for any of the pictures on a particular pageid, the entire page is rendered as a table with captions, with the thumbnails linking to individual pages for each picture. Otherwise, the thumbnails are placed multiple to a line, linking directly to the full size images themselves. _ imagename.jpeg WIDTH=xxx HEIGHT=yyy These entries are autogenerated to cache information that makes the generated HTML more efficient. Ignore them unless you just resized an image and need to elide the existing cache information for it and its thumbnail. Delete the thumbnail itself to get a new one made. pageid title Title Text Sets the title for the named page. pageid intro Introductory Text Adds text to the top of the index page, before the pictures. pageid tail Trailer Text Adds text to the end of an index page. index auto yes Autogenerates an index.html page. (Alternatively, you could treat `index' as a regular pageid and manually place images on it.) pageid indexhead Your HTML goes here Adds arbitrary HTML to the <HEAD> of pageid.html. This is an ideal place to put a stylesheet link, if you're so inclined, and if you want to pass validator.w3.org, you may need to specify a charset. (See also the DOCTYPE definition.) pageid pichead Your HTML goes here Like indexhead, but adds to the <head> of individual picture pages subordinate to `pageid'. pageid tail Your HTML goes here Adds arbitrary HTML to the bottom of pageid.html and any subordinate pages, just before </BODY>. (I use this for a copyright notice.) Don't worry if this seems a bit overpoweringly complex to start with; to get started, you can just run the script and start hacking at `.data'. For example, you could take a peek at the file in my gallery [1], and adb's [2] are similar (though they don't currently use the extra features I've added to the script). [1] http://www.chiark.greenend.org.uk/~ryounger/gallery/.data [2] http://www.leftmind.net/~adb/treb/.data http://www.leftmind.net/~adb/ols2001/.data You can change the DOCTYPE tag; it's globally hard-wired at the top of the script. Prerequisites: * perl. v5.6.1 works for me, probably any v5.*, quite possibly older versions. * djpeg, cjpeg and pnmscale on your path, for thumbnail creation. (On my Debian box these are provided by installing the netpbm and libjpeg-progs packages.) The script is probably rather brittle. If it breaks, you get to keep both bits. (It works for me, and I'm exposing my changes in case they're useful. In case of doubt, refer to the GPL. By all means hack to suit; that's how this incarnation came about in the first place.) Enjoy! - wry, 2003.03.04