#!/usr/local/bin/perl5 # gallery.pl: picture gallery generator my($version) = "1.1.25"; # # (c) 2002-2005 by Mihai Budiu, mihaib from cs.cmu.edu # http://www.cs.cmu.edu/~mihaib/ftp/gallery.pl # see http://cobra.crcl.cs.cmu.edu:8000/favorites for a sample gallery # usage: just run in a directory containing pictures, twice # This version works somewhat on windows as well # needs: - perl # - ImageMagick http://imagemagick.org # - jhead http://www.sentex.net/~mwandel/jhead (optional) # (jhead extracts photo information from jpeg files) # TODO # - take care not to delete any user files # - make a button to resize the web page to "ideal" dimensions # - option to create a separate directory hierarchy # - print on top the position in the hierarchy # - handle gracefully missing files # - BUG: if you use '*' commands in verbatim the program gets confused # - print the resolution next to "hi-res" use strict; RESTART: # feel free to edit the copyright, or to replace the generated file my($copyright) = 'These pictures are (c) 2002 by Mihai Budiu. However, you can use them freely if you like them. '; # configuration variables; these can be changed through command-line options too my(%image_suffixes) = ("jpg" => 1, "gif" => 1, "jpeg" => 1, "png" => 1, "tif" => 1); my(%options) = ( imgsizedescr => 0, # add image size information to each pic copyright => 0, # print copyright thumbx => 128, thumby => 128, picx => 800, picy => 700, recurse => 0, # descend directories recursively master_title => "Web album", # title of master page useexif => 0, # exif is image info embedded jpegs havejhead => 1, # jhead program is available (to read exif file info) style => 'plain', # default style cleanup => 0, # delete all byproducts of gallery.pl (but not descriptions.txt) sharpen => "-sharpen 60", # default sharpen arguments (for old ImageMagick) verbose => 0, sortdate => 0, # sort pics on date when generating descriptions.txt nooriginal => 0, # do not create a link to the original large image rows => 0, sanitize => 0, # fix image names containing funny characters unused => 0, # list image files not mentioned in descriptions.txt cleanunused => 0, # remove unused image files and their derivatives addnewpics => 0, # add new images at the end of descriptions.txt sametitle => 0, # all pages should have the same title nocaption => 0, # don't put captions to figures skip => "", # don't descend these directories onepic => "", # only reprocess this picture reconstruct => 0, # rebuild from thumb_ and v_, without originals ); my(%page) = ( # table representing a html page columns => 3, # number of columns crt_col => 0, # current column started => 0, # started printing pic_cols => 1, # columns for next picture crt_pic => 1, # current picture number (do not initialize) skipping => 0, crt_sec => 1, # current section number printed_title => 0, # title has already been printed filename => "", inverbatim => 0 # processing 'verbatim' command ); my(@styles) = ('simple', 'frame', 'plain'); # legal styles my($commandline) = "gallery.pl " . join(' ', @ARGV); # save it here my($sysname) = $^O; print "Running on $sysname\n"; my($login) = "Windows user"; if (! ($sysname =~ /Win/)) { $login = getpwuid($<) || "?"; } my($today) = &getdate(); # parse command line arguments sub usage { print << "EOO"; gallery.pl version $version gallery.pl creates a 'descriptions.txt' file if none exists gallery.pl uses the commands in 'descriptions.txt' to create HTML pages with pictures Usage: gallery.pl [options] The recognized options are: -r - descend recursively subdirectories -v - verbose -suff suffix- this suffix indicates image files too -rerun - use exact same arguments as last time -skip dirs - don't descend these dirs (comma-separated list) -onepic pic - recreate thumbnails just for this picture -clean - remove all gallery.pl-generated files (but not descriptions.txt) -h, -help - print this help and exit layout options -tx pixels - max x size of thumbnails (default $options{thumbx}) -ty pixels - max y size of thumbnails (default $options{thumby}) -x pixels - max x size of pics (default $options{picx}) -y pixels - max y size of pics (default $options{picy}) -columns n - columns in each page (default $page{columns}) -rows n - up to this many table rows per thumbnail page (default unbounded) -info - read EXIF info from pictures -noorig - do not link to original image -style s - use this style (see below) -sanitize - sanitize url extracted from pic name (remove funny chars) -imgsz - embed images size information in html (looks better during download) -copyright - print copyright notice on generated pages -nocaptions - generate empty captions for pictures descriptions.txt options (i.e. which don't generate HTML) -unused - list pictures not mentioned in descriptions.txt -cleanunused- remove pictures not mentioned and thumbnails -addnewpics - append new pictures to descriptions.txt -sortdate - sort pictures in descriptions.txt on date (default is on name) Legal descriptions.txt lines (special commands start * in first column): * title * section * columns * titlepic * file * skip * pagebreak * verbatim Everything between these two commands is literally copied in the HTML file * endverbatim Text with no * is considered caption for previous image. The html table of contents can be in one of these styles (-style argument): EOO foreach (@styles) { print STDERR "\t$_\n"; } exit 1; } while ($ARGV[0] =~ /^[-]/) { $_ = $ARGV[0]; OPTION: { /^-tx$/ && ($options{thumbx} = $ARGV[1], shift, last OPTION); /^-ty$/ && ($options{thumby} = $ARGV[1], shift, last OPTION); /^-onetitle$/ && ($options{samtitle} = 1, last OPTION); /^-x$/ && ($options{picx} = $ARGV[1], shift, last OPTION); /^-y$/ && ($options{picy} = $ARGV[1], shift, last OPTION); /^-columns$/ && ($page{columns} = $ARGV[1], shift, last OPTION); /^-r$/ && ($options{recurse} = 1, last OPTION); /^-suff$/ && ($image_suffixes{$ARGV[1]} = 1, shift, last OPTION); /^-info$/ && ($options{useexif} = 1, last OPTION); /^-style$/ && ($options{style} = $ARGV[1], shift, last OPTION); /^-v$/ && ($options{verbose} = 1, last OPTION); /^-noorig$/ && ($options{nooriginal} = 1, last OPTION); /^-sanitize$/&& ($options{sanitize} = 1, last OPTION); /^-clean$/ && ($options{cleanup} = 1, last OPTION); /^-rows$/ && ($options{rows} = $ARGV[1], shift, last OPTION); /^-unused$/ && ($options{unused} = 1, last OPTION); /^-cleanunused$/ && ($options{cleanunused} = 1, last OPTION); /^-addnewpics$/ && ($options{addnewpics} = 1, last OPTION); /^-nocaptions$/ && ($options{nocaption} = 1, last OPTION); /^-imgsz$/ && ($options{imgsizedescr} = 1, last OPTION); /^-sortdate$/ && ($options{sortdate} = 1, last OPTION); /^-copyright$/ && ($options{copyright} = 1, last OPTION); /^-rerun$/ && (@ARGV = &reset_argv(), print join('', @ARGV), goto RESTART); /^-skip$/ && ($options{skip} = $ARGV[1], shift, last OPTION); /^-reconstruct$/ && ($options{reconstruct} = 1, last OPTION); /^-onepic$/ && ($options{onepic} = $ARGV[1], shift, last OPTION); /^-he?l?p?$/ && (&usage(), shift, last OPTION); print STDERR << "EOI"; gallery.pl version $version: illegal flag '$_' gallery.pl creates a 'descriptions.txt' file if none exists gallery.pl uses the commands in 'descriptions.txt' to create HTML pages with pictures Quick usage: (1) make a backup copy of the directory with images (2) run gallery.pl to generate descriptions.txt (3) edit descriptions.txt file to your taste (4) run gallery.pl again to generate html Use -h to get a detailed help EOI exit 1; } shift(@ARGV); } # globals my(%crtpage) = %page; my(%masterpage) = %page; # master index page my($totpics)=0; # total pictures my($depth)=0; # for recursive directory traversals my(@crtdir) = (); # stack of directories in depth-first traversal my(@pics) = (); # pictures in current directory my(@secs) = (); # sections in current directory my(@captions) = (); # image captions my(@secno) = (); # line where each section begins my(@secpage) = (); # for each section, thumbnail page where it resides my($crtpage, $totpages) = (0,0);# current thumbnail page, and total # my($lasttitle) = ""; # last title printed # global file descriptors: # - I for index.html # - MASTER_INDEX for recursive traversals # - IMG for the current image &check_programs(); # find out which programs aren't installed my($header) = ' $title '; my($title_header) = '

$title

'; my($trailer) = " "; my($css) = " /* gallery.css -- default gallery.pl style sheet; please edit to your own taste */ BODY, P, TD, TH, UL, OL, H1, H2, H3, H4, PRE, SMALL, BIG, A, TABLE, TD, TR, TH { font-family : Helvetica, sans-serif; background-color : black; color : white; } DD { margin: 1em; } A:LINK { text-decoration : underline; color : white; } A:VISITED { text-decoration : underline; color : #eee; } A:HOVER { text-decoration : underline; color : green; } "; ################################## sub reset_argv { # look for command line arguments in master_index.html or index.html my $searchin; if (-f "master_index.html") { $searchin = "master_index.html"; } elsif (-f "index.html") { $searchin = "index.html"; } else { die "Error: no *_index.html file to look in for previous arguments\n"; } open(S, "< $searchin") || die "** Cannot read $searchin\n"; my @match = ; close(S); @match = grep /command line:/, @match; if ($#match == -1) { die "** Could not find previous arguments in $searchin\n"; } $_ = $match[0]; s/^.*gallery\.pl//; s/\s*-->$//; print "gallery.pl $_\n"; return split; } sub getpicdate { # using exif information return picture date my($pic) = $_[0]; if (!$options{havejhead}) { return 0; } my @info = `jhead $pic`; @info = grep /Date\/Time/, @info; my $date = $info[0]; my($year, $month, $day, $hour, $min, $sec) = $date =~ /(\d+):(\d+):(\d+)\s+(\d+):(\d+):(\d+)$/; my $retval = sprintf("%04d:%02d:%02d:%02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec); # print "$pic $retval\n"; return $retval; } sub getdate { my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); $year += 1900; return "$months[$mon] $mday, $year"; } sub sanitize_url { # TODO if (! $options{sanitize}) { return $_[0]; } $_ = $_[0]; s/ /\%20/g; return $_; } sub header { # compute the header of an html page my($title) = $_[0]; my $myheader = $header; $myheader =~ s/\$title/$title/g; $myheader =~ s/\$version/$version/; $myheader =~ s/\$today/$today/; $myheader =~ s/\$login/$login/; $myheader =~ s/\$commandline/$commandline/; # relative link to .css my($depth) = $#crtdir; my($reverse) = "../" x $depth; $myheader =~ s/(gallery.css)/$reverse\1/; # replace link to css return $myheader; } sub cpsystem { # print, execute system(), check results my($c) = $_[0]; if ($options{verbose}) { print " Executing '$c'\n"; } system($c); if ($?) { die "** Command $c returned " . $? . "\n"; } } sub cleanup { # remove all gallery.pl-generated files, except descriptions.txt &cpsystem("rm -f thumb_* v_* *.*.html index.html index_[0-9]*.html gallery.css copyright.html"); } sub write_html_img { # write an my($image, $border, $file) = @_; my($x, $y) = ("", ""); if ($options{imgsizedescr}) { if (! -f $image) { # print STDERR "## Cannot read image $image\n"; # Sometimes the image directory is not relative to the current directory, # and then the file is not found. I am too lazy to fix this now. goto writeit; } my @identify = `identify -verbose \"$image\"`; @identify = grep /geometry/, @identify; $#identify > -1 || die "** Can't read $image geometry using 'identify'\n"; my($size) = $identify[0]; if ($?) { # print STDERR "## Cannot read image size information for $image\n"; goto writeit; } ($x, $y) = $size =~ /(\d+)x(\d+)/; $x = "WIDTH=$x"; $y = "HEIGHT=$y"; } writeit: print $file "\"\""; } sub shrink { # shrink a picture using ImageMagick convert my($img) = $_[0]; my($command); if (! -f "thumb_$img" || (-M "thumb_$img" > -M $img)) { $command = "convert $options{sharpen} -geometry $options{thumbx}" . "x" . "$options{thumby} $options{sharpen} \"$img\" \"thumb_$img\""; &cpsystem($command); } if ($options{style} ne "plain" && (! -f "thumb_small_$img" || (-M "thumb_small_$img" > -M $img))) { # plain style doesn't use the small thumbnails $command = "convert $options{sharpen} -geometry " . $options{thumbx}/2 . "x" . $options{thumby}/2 . " $options{sharpen} \"$img\" \"thumb_small_$img\""; &cpsystem($command); } if (! -f "v_$img" || (-M "v_$img" > -M $img)) { my(@tmp) = `identify -verbose \"$img\"`; @tmp = grep /[Gg]eometry/, @tmp; my($size) = $tmp[0]; my($x, $y) = $size =~ /(\d+)x(\d+)/; # shrink only if too big if ($x >= $options{picx} || $y >= $options{picy}) { $command = "convert -geometry $options{picx}" . "x" . "$options{picy} \"$img\" \"v_$img\""; &cpsystem($command); } } } sub do_exif { # extract exif image information from an image # save it in an info_$image.html file. Return 1 if success my($image) = $_[0]; if (! $options{havejhead}) { return 0; } if (! $image =~ /jpe?g/) { # exif cannot handle non-jpeg images return 0; } open(INFO, "> info_$image.html") || die "Can't write exif info file info_$image.html\n"; print INFO &header("Info"); open(E, "jhead \"$image\" |"); my(@exif) = ; close(E); pop(@exif); print INFO "
\n"; print INFO "

Original image info

\n \n"; foreach (@exif) { if (/omment/) { next; } my($l, $r); $l = substr($_,0,13); $r = substr($_,13,-1); print INFO " " . "\n"; } print INFO "
$l$r
\n"; print INFO " Close\n"; print INFO "
"; print INFO $trailer; close(INFO); return 1; } sub plain_part { # draw the plain part: common between frame & plain styles my($image, $caption, $indent) = @_; $indent = ' ' x $indent; my($p); { print IMG "$indent\n"; print IMG "$indent\n"; print IMG "$indent \n"; if ($crtpage{crt_pic} > 1) { $p = $pics[$crtpage{crt_pic} - 2]; # indexing starts from 0 $p = &sanitize_url($p); print IMG "$indent \n"; } else { print IMG "$indent \n"; } print IMG "$indent \n"; if ($crtpage{crt_pic} <= $#pics) { $p = $pics[$crtpage{crt_pic}]; $p = &sanitize_url($p); print IMG "$indent \n"; print IMG "$indent \n"; } print IMG "$indent \n"; # check if original image is different from the current my($sane_image) = $image; if (-f "v_$image" && ! $options{nooriginal}) { $sane_image = &sanitize_url($image); print IMG "$indent \n"; } if (&do_exif($image)) { print IMG "$indent \n"; } print IMG "$indent \n"; print IMG "$indent
PreviousPreviousUpNext  hi-resinfo
\n"; } } sub frame_style { # do a image page in a frame-style my($image, $caption) = @_; print IMG "

$caption

\n"; print IMG "

\n"; print IMG " \n"; print IMG " \n"; print IMG " \n"; print IMG " \n"; print IMG " \n"; print IMG " \n"; } print IMG " \n
Select\n"; plain_part(@_, 5); print IMG "
\n"; { # the vertical icons bar print IMG " \n"; print IMG " \n"; my($p, $nr, $sec) = ("", 0, 0); for $p (@pics) { my($border, $color) = (0,"black"); if ($p eq $image) { $border=4; $color = "white"; } if ($secno[$nr + $sec]) { my($indexfile) = $secpage[$sec]; my($indexfilename) = &indexhtmlname($indexfile); print IMG " \n"; $sec++; } # print STDERR "$p $sec $crtpage{crt_sec}\n"; if ($sec+1 == $crtpage{crt_sec}) { $p = &sanitize_url($p); print IMG " \n"; } $nr++; } print IMG "
" . @secs[$sec] . "
"; &write_html_img("thumb_small_$p", $border, *IMG); print IMG "
\n"; } { print IMG " \n"; print IMG "
"; my $useimg = "v_$image"; if (! -f "v_$image") { # no shrunk version: use the image itself $useimg = "$image"; } &write_html_img($useimg, 0, *IMG); print IMG "
\n"; } sub plain_style { # do an image page in a totally plain style: the picture and some links only my($image, $caption) = @_; print IMG "

$caption

\n"; print IMG "

\n"; print IMG " \n"; print IMG " "; { # the image itself print IMG "\n"; } print IMG "
\n"; plain_part(@_, 4); print IMG "
"; my $useimg = "v_$image"; if (! -f "v_$image") { # no shrunk version: use the image itself $useimg = "$image"; } &write_html_img($useimg, 0, *IMG); print IMG "
\n"; } sub simple_style { # do an image page in a simple style: prev, next, etc. links, but with some thumbnails my($image, $caption) = @_; my($p); print IMG "

$caption

\n"; print IMG " "; my $useimg = "v_$image"; if (! -f "v_$image") { # no shrunk version: use the image itself $useimg = "$image"; } &write_html_img($useimg, 0, *IMG); print IMG "\n"; print IMG "

\n \n"; print IMG " \n"; if ($crtpage{crt_pic} > 1) { $p = $pics[$crtpage{crt_pic} - 2]; # indexing starts from 0 $p = &sanitize_url($p); print IMG " \n"; } print IMG " \n"; if ($crtpage{crt_pic} <= $#pics) { $p = $pics[$crtpage{crt_pic}]; $p = &sanitize_url($p); print IMG " \n"; } my($sane_image) = $image; if (&do_exif($image)) { $sane_image = &sanitize_url($image); print IMG " \n"; } print IMG " \n"; print IMG " \n
< Back"; &write_html_img("thumb_small_$p", 0, *IMG); print IMG "
previous
"; &write_html_img("thumb_small_$image", 0, *IMG); print IMG "
up
"; &write_html_img("thumb_small_$p", 0, *IMG); print IMG "
next
infooriginal
\n"; } sub print_img_in_page { # print an image thumbnail in a table my($link) = shift; my($pic) = shift; my($pg) = shift; my($filedesc) = $$pg{file}; defined($filedesc) || die "** undefined file descriptor\n"; if (! $$pg{begun_table}) { print $filedesc " \n"; $$pg{begun_table} = 1; $$pg{crt_col} = 0; print $filedesc " "; } elsif (! $$pg{crt_col}) { print $filedesc "\n \n "; } $$pg{crt_col} = ($$pg{crt_col}+$$pg{pic_cols}) % $$pg{columns}; my($link1, $link2); if ($link ne "") { $link1 = ""; $link2 = ""; } else { $link1 = ""; $link2 = ""; } print $filedesc "\n "; } do_image($image); $totpics++; } elsif (/^columns\s+/) { s/^columns\s+//; &set_columns_in_page($_, \%crtpage); } elsif (/^pagebreak/) { &close_table_in_page(\%crtpage); close(I); rename($crtpage{filename}, $crtpage{filename} . ".bak"); rename($crtpage{filename} . ".new", $crtpage{filename}); ++$crtpage; my($newpagename) = &indexhtmlname($crtpage); open(I, "> $newpagename.new") || die "Error: can't create new index page\n"; my($savecrtpic) = $crtpage{crt_pic}; my($savecrtsec) = $crtpage{crt_sec}; %crtpage = %page; $crtpage{crt_pic} = $savecrtpic; $crtpage{crt_sec} = $savecrtsec; $crtpage{filename} = $newpagename; $crtpage{file} = *I; # recurse &command("* title $lasttitle"); } elsif (/^verbatim/) { &close_table_in_page(\%crtpage); print I "\n"; $crtpage{inverbatim} = 1; } elsif (/^endverbatim/) { warn "endverbatim without verbatim; ignoring\n"; } elsif (/^titlepic\s+/) { s/^titlepic\s*//; my($pic) = $_; if ($options{recurse}) { my($align); my($dir) = join("/", @crtdir); $dir = &sanitize_url($dir); if ($#crtdir == 1) { if ($pic =~ /^\s*$/) { # print "EMPTY TITLEPIC\n"; &print_img_in_page("$dir/$crtpage{filename}", "", \%masterpage); } else { &print_img_in_page("$dir/$crtpage{filename}", "$dir/thumb_$_", \%masterpage); } } } # else do nothing } elsif (/^skip\s*/) { &print_img_in_page("", "", \%crtpage); # print just a space; no link } else { print STDERR "## Unrecognized command $_ in 'descriptions.txt'\n"; } } sub basename { my $file = $_[0]; if ($sysname =~ /Win/) { $file =~ s/.*\\//; } else { $file =~ s/.*\///; } $file =~ s/\.*//; return $file; } sub pwd { if ($sysname =~ /Win/) { return `cd`; } return `pwd`; } sub create_descriptions { # create a descriptions.txt file containing all the images in the directory open(DESC, "> descriptions.txt") || die "Error: can't write in 'descriptions.txt'"; my($crtdir) = &pwd(); $crtdir = &basename($crtdir); chop $crtdir; print DESC "# descriptions.txt: this file has been automatically created\n", "# by gallery.pl, http://www.cs.cmu.edu/~mihaib/ftp/gallery.pl\n", "# on $today by $login\n", "#\n", "# Comments start with #\n", "# Use '* columns n' to change number of columns of a picture\n", "# Use '* pagebreak' to start a new thumbnails page\n", "# Use '* skip' to insert empty 'pictures'\n", "# Use '* verbatim...* endverbatim' to insert arbitrary inter-picture html\n\n", "* title $crtdir\n", "* section $crtdir\n"; opendir(DIR, ".") || die "Error: can't read current directory"; my @files = readdir(DIR); if ($options{sortdate}) { my (%dates); print "Extracting dates (may take some time)\n"; map { $dates{$_} = &getpicdate($_) } @files; # print join "\n", (keys(%dates)); @files = sort { $dates{$a} cmp $dates{$b} } @files; } else { @files = sort @files; } closedir(DIR); my($pics, $f, $suff, $first) = (0, "", "", 1); for $f (@files) { # if (! -f $f) { next; } my($file, $process) = &is_pic_name($f); if (! $process) { next; } if ($first) { print DESC "* titlepic $f\n# Picture to put in master index when recursing\n"; print DESC "# titlepic, if present, MUST be after '* title'\n\n"; } print DESC "* file $f\n"; if ($first) { print DESC "# Add on next line text describing $f\n"; $first = 0; } if (! $options{nocaption}) { print DESC "$file\n\n"; } $pics++; } if (! $pics) { print "Warning: no pictures found!\n"; close(DESC); unlink("descriptions.txt"); return 0; } else { close(DESC); return 1; } } sub addcol { # compute new position in table after skipping $skip columns my($col, $row, $maxcol, $maxrow, $skip) = @_; my($newtable) = 0; $col += $skip; if ($col > $page{columns}) { $row++; $col = 1; if (($options{rows} > 0) && ($row > $options{rows})) { $row = 1; $newtable = 1; } } # print "($col $row)\n"; return ($col, $row, $newtable); } sub print_copyright { my($file) = $_[0]; if ($options{copyright}) { print $file "
copyright\n"; } } sub create_index { # create the index.html file from the descriptions.txt file open(F, "< descriptions.txt") || die "Error: can't read descriptions.txt\n"; my(@desc) = ; close(F); # remove comments and empty lines grep s/\#.*//, @desc; # remove comments @desc = grep !/^\s*$/, @desc; # empty lines grep chop, @desc; grep(s/\s+$//, @desc); # extract picture names @pics = grep(/^\*\s*file/, @desc); grep(s/\*\s*file\s*//, @pics); $crtpage{crt_pic} = 1; # extract section names @secs = grep(/^\*\s*section/, @desc); grep(s/\*\s*section\s*//, @secs); # which lines are section numbers @secno = grep(/^\*\s*(section|file)/, @desc); @secno = map (/^\*\s*section/ && $crtpage{crt_sec}++, @secno); $crtpage{crt_sec} = 1; $crtpage = 0; # if we indicated we want up to a fixed number of rows, compute now where and insert the page breaks if ($options{rows}) { my($crtrow, $crtcol,$colsnextpic,$newtable) = (1,1,1,0); my(@desccopy) = @desc; my($index) = 0; foreach (@desccopy) { if (/\*\s*file/ || /\*\s*skip/) { ($crtcol, $crtrow, $newtable) = &addcol($crtcol, $crtrow, $options{columns}, $options{rows}, $colsnextpic); $colsnextpic = 1; if ($newtable) { # search for next command and insert a page break just before it do { $index++; if ($index > $#desc) { goto finished_page_breaks; } } while (! ($desc[$index] =~ /^\*/)); splice(@desc, $index, 0, "* pagebreak"); } } elsif (/\*\s*columns/) { my($colsnextpic) = $_ =~ /\*\s*columns (\d+)/; } elsif (/\*\s*pagebreak/) { ($crtrow, $crtcol) = (1,1); } elsif (/\*\s*section/) { if ($crtcol > 1) { $crtrow++; $crtcol = 1; if ($crtrow > $options{rows}) { $crtrow = 1; splice(@desc, $index, 0, "* pagebreak"); $index++; } } } $index++; } } finished_page_breaks: # sections are separated by pagebreaks, find how @secpage = (); my($page) = 0; foreach (@desc) { if (/^\*\s*pagebreak/) { $page++; } elsif (/^\*\s*section/) { push(@secpage, $page); # print "$#secpage -> $page\n"; } } # extract caption content # This is harder... my($text) = join('#', @desc) . "#"; # print "$text\n"; @captions = split(/\*\s*file[^\#]*\#/, $text, -1); @captions = @captions[1..$#captions]; # drop up to first figure push(@captions, ""); # last one may be missing, add there to be safe grep(s/\*.*//g, @captions); # remove other formatting elements # print join("\n ", @captions), "\n"; grep(s/\#//g, @captions); # remove remaining sharp separators if ($options{nocaption}) { # ignore everything, but keep right number grep($_ = "", @captions); } $totpages = grep(/\*\s*pagebreak/, @desc); # do we have a titlepic: if so, move above title # otherwise, generate an empty titlepic my(@tmp) = grep(/^\*\s*titlepic/, @desc); @desc = grep(! /^\*\s*titlepic/, @desc); # remove them my($titlepics) = $#tmp; if ($titlepics > 0) { print STDERR "## Cannot have more than 1 titlepic per directory\n"; } elsif ($titlepics == -1) { # no titlepic: put an empty one unshift(@desc, "* titlepic "); } else { unshift(@desc, $tmp[0]); } open(I, "> index.html.new") || die "Error: can't write index.html.new"; $crtpage{file} = *I; $crtpage{filename} = "index.html"; foreach (@desc) { # each line is processed independently if ($crtpage{inverbatim}) { # lines between * verbatim ... * endverbatim are special if (/^\*\s*endverbatim/) { $crtpage{inverbatim} = 0; print $_, "\n"; print I "\n"; } else { print I $_, "\n"; } } elsif (/^\*/) { # lines beginning with "*" are commands &command($_); } else { # everything else is copied literally in the result # (you can embed html here is you want) if (! $crtpage{skipping} && ! $options{nocaption}) { print I $_; } } } if ($crtpage{inverbatim}) { warn "verbatim without endverbatim encountered!"; $crtpage{inverbatim} = 0; } &close_table_in_page(\%crtpage); &print_copyright(*I); print I $trailer; close(I); rename($crtpage{filename}, $crtpage{filename} . ".bak"); rename($crtpage{filename} . ".new", $crtpage{filename}); } sub list_unused { # list figures in current directory not mentioned in descriptions.txt if (! -f "descriptions.txt") { print STDERR "## no descriptions.txt here\n"; return; } open(D, "< descriptions.txt") || die "** Can't read 'descriptions.txt'\n"; my(@mentioned) = ; close(D); @mentioned = grep /^\*\s*file/, @mentioned; grep(s/\*\s*file\s+//, @mentioned); grep(chop, @mentioned); grep(s/\s+$//, @mentioned); my(%mentioned); map( ($mentioned{$_} = 1), @mentioned); # now read picture files opendir(DIR, ".") || die "Error: can't read current directory"; my @files = readdir(DIR); closedir(DIR); if ($options{addnewpics}) { open(D, ">> descriptions.txt") || die "Error: can't append new info to 'descriptions.txt'\n"; print D "\n#### New pictures, added $today by $login\n\n"; } my(@toremove) = (); my($f); my($found) = 0; for $f (@files) { my($file, $process) = &is_pic_name($f); if (! $process || $mentioned{$f}) { next; } if ($options{unused}) { $found++; print " $f\n"; } push (@toremove, $f); if ($options{addnewpics}) { $found++; print D "* file $f\n"; if (! $options{nocaption}) { print D "$file\n\n"; } } } print STDERR "$found pictures found\n"; # print on stderr so we can use the stdout output in scripts if ($options{addnewpics}) { close D; } if ($options{cleanunused} && ($#toremove >= 0)) { my $command = ""; print "Removing in 5 seconds the following files (press ^C to cancel)\n"; foreach (@toremove) { my $s = "$_ v_$_ info_$_.html thumb_$_ $_.html thumb_small_$_"; print "\t$s\n"; $command .= " $s"; } sleep 5; &cpsystem("rm -f $command"); } } sub do_directory { # process the indicated directory my($dir) = $_[0]; push(@crtdir, $dir); %crtpage = %page; my($save) = &pwd(); chop $save; if (! -d $dir) { die "Error: can't find $dir directory"; } chdir($dir); my (%skipdirs) = (); map ($skipdirs{$_} = 1, split(",", $options{skip})); if ($skipdirs{$dir}) { print STDERR "Skipping $dir\n"; goto done; } elsif ($options{cleanup}) { &cleanup(); } elsif ($options{unused} || $options{addnewpics} || $options{cleanunused}) { &list_unused(); } elsif (-f "descriptions.txt") { &create_index(); } else { if (&create_descriptions()) { print "I have created a skeleton 'descriptions.txt' file in $dir\n", "Please edit it by hand and run 'gallery.pl' again\n"; } } if ($options{recurse}) { opendir(DIR, "."); my(@files) = sort(readdir(DIR)); closedir(DIR); @files = grep(!/^\./, @files); @files = grep(-d $_, @files); if ($#files > -1) { $depth++; for (@files) { print STDERR "Doing directory $_\n"; &do_directory($_); } $depth--; } } done: chdir($save); pop(@crtdir); } sub main { # create the style sheet if (! -f "gallery.css") { open(F, "> gallery.css") || die "Error: can't write style sheet\n"; print F $css; close(F); } if ($options{copyright} && ! -f "copyright.html") { open(F, "> copyright.html") || die "Error: can't write copyright file\n"; print F &header("Copyright"); print F $copyright; print F $trailer; close F; } if ($options{onepic} ne "") { # create a fake command and execute it &shrink($options{onepic}); return; } my $time = time(); my($create_master_index) = $options{recurse} && !$options{cleanup} && !$options{unused} && !$options{addnewpics}; if ($create_master_index) { open(MASTER_INDEX, "> master_index.html.new") || die "Error: can't write master index\n"; $masterpage{file} = *MASTER_INDEX; print MASTER_INDEX &header($options{master_title}); print MASTER_INDEX "

$options{master_title}

\n"; } if ($options{cleaup} && $options{recurse}) { &cpsystem("rm -f master_index.html"); } &do_directory("."); if ($create_master_index) { &close_table_in_page(\%masterpage); &print_copyright(*MASTER_INDEX); print MASTER_INDEX $trailer; close(MASTER_INDEX); rename("master_index.html", "master_index.html.bak"); rename("master_index.html.new", "master_index.html"); } $time = time() - $time; print "Total pictures processed: $totpics in $time seconds\n"; } # program &main(); sub check_programs { # check required programs if ($sysname =~ /Win/) { $options{havejhead} = 0; } else { my @tmp = `jhead -h`; if ($?) { if ($options{useexif}) { warn "## Cannot find jhead program used to extract EXIF picture information\n"; } $options{havejhead} = 0; } } # determine convert options; unfortunately these have changed at some point my @tmp = `convert -h`; if ($?) { die "Error: 'convert' program not found"; } @tmp = grep /sharp/, @tmp; if ($tmp[0] =~ /geometry/) { # modern convert $options{sharpen} = "-sharpen 3x1"; } @tmp = `identify -help`; if ($?) { die "Error: 'identify' program not found"; } }
${link1}"; if ($pic ne "") { &write_html_img("$pic", 0, $filedesc); } else { print $filedesc " "; } print $filedesc "${link2}\n

"; $$pg{pic_cols} = 1; # reset number of columns taken by next picture } sub set_columns_in_page { # next object will use this many columns my($cols) = shift; my($pg) = shift; $$pg{pic_cols} = $cols; if ($cols > $$pg{columns}) { print STDERR "Cannot use more columns ($cols) than maximum ($$pg{columns})\n"; $$pg{pic_cols} = $$pg{columns}; } } sub close_table_in_page { # finished with a object my($pg) = $_[0]; my($file) = $$pg{file}; if ($$pg{begun_table} != 0) { print $file "\n \n \n
\n\n"; $$pg{begun_table} = 0; } } sub is_pic_name { my($filename) = $_[0]; if ($filename =~ /^thumb_/) { return ("", 0); } if ($filename =~ /^v_/) { return ("", 0); } my($retval) = $filename; $retval =~ tr/A-Z/a-z/; my ($suff) = $retval =~ /\.(\w+)$/; if (! $image_suffixes{$suff}) { return ("", 0); } $retval =~ s/\.$suff$//; return ($retval, 1); } sub do_image { # process one image (a * file command) my($image) = $_[0]; # check if image actually exists if ((! -f $image) && (! $options{reconstruct})) { print STDERR "## warning: Cannot find '$image'; skipping\n"; # this will signal to skip next caption too $crtpage{skipping} = 1; return; } else { $crtpage{skipping} = 0; } chmod 444, $_; if (! $options{reconstruct}) { &shrink($image); } $image = &sanitize_url($image); my $picname = $pics[$crtpage{crt_pic} - 1]; if ($picname ne $image) { die "Error: inconsistent picture numbering '$image' != '$picname'\n"; } ($crtpage{crt_pic} <= $#captions+1) || die "Error: mismatch in caption numbering\n"; my($caption) = @captions[$crtpage{crt_pic} - 1]; &print_img_in_page("$image.html", "thumb_$image", \%crtpage); # for each image create a html file with prev, up, next links using thumbnails open(IMG, "> $image.html") || warn "Error: Can't write in $image.html"; if ($caption =~ /^\s*$/ && $#secs > 0) { $caption = $secs[$crtpage{crt_sec}-2]; # crt_sec starts at 1; also, crt_sec is incremented when *section is met, # so the first section is really #2! } print IMG &header($options{sametitle} ? $options{master_title} : $caption); print IMG "

\n"; if ($options{style} eq 'simple') { &simple_style($image, $caption); } elsif ($options{style} eq 'frame') { &frame_style($image, $caption); } elsif ($options{style} eq 'plain') { &plain_style($image, $caption); } else { die "Error: unknown style $options{style}; choose from " . join(',', @styles) . "\n"; } $crtpage{crt_pic}++; # increment if printing a new picture print IMG "
"; print IMG $trailer; close IMG; } sub indexhtmlname { # generate name of thumbnail page my($nr) = $_[0]; if ($nr == 0) { return "index.html"; } return "index_$nr.html"; } sub command { # process a descriptions.txt command $_ = $_[0]; print $_, "\n"; s/^\*\s*//; if (/^title\s+/) { if ($crtpage{printed_title}) { print STDERR "## Title already printed"; return; } s/^title\s+//; $lasttitle = $_; my($mytitle_header) = $title_header; if ($totpages) { $_ .= " [" . ($crtpage+1) . "/" . ($totpages+1) . "]"; } $mytitle_header =~ s/\$title/$_/g; print I &header($_), $mytitle_header; # print table of contents if ($#secs > 0) { # if only one section, there's no point print I " \n"; print I "
    \n"; my($n)=0; foreach (@secs) { my($file) = &indexhtmlname($secpage[$n]); print I "
  • $_\n"; $n++; } print I "
\n"; print I "
\n\n"; } if ($options{recurse} && ($#crtdir == 1)) { # shallow hierarchy in master_index my $dir = join("/", @crtdir); $dir = &sanitize_url($dir); if ($crtpage == 0) { s/\s*\[1\/\d+\]$//; print MASTER_INDEX "$_\n"; } print I " Home\n"; } if ($totpages) { if ($crtpage > 0) { my($indexname) = &indexhtmlname($crtpage-1); print I " Previous"; } if ($crtpage < $totpages) { my($indexname) = &indexhtmlname($crtpage+1); print I " Next"; } print I "
\n"; } } elsif (/^section\s+/) { if ($#secs > 0) { s/^section\s+//; &close_table_in_page(\%crtpage); print I " \n

$_

\n"; } ++$crtpage{crt_sec}; } elsif (/^file\s+/) { s/^file\s+//; my $image=$_; if ($crtpage{begun_table}) { print I "\n