#!/usr/bin/perl -s- # -*-Perl-*- ###################################################################### # pcdindex # Rourke McNamara # Wed Aug 7 00:18:12 EDT 1996 # meng weng wong # Fri Apr 10 00:35:49 EDT 1998 # # $Id: pcdindex,v 1.1 1998/04/17 05:19:49 mengwong Exp mengwong $ # # makes html image index of all the files in a directory # # usage: pcdindex pcd_dir [...] # eg: pcdindex 3378 2053 # input: no stdin expected. knows to read captions.txt. # # output: writes a bunch of html files in the indicated directory that # "cover" the images in a sort of photo-gallery format. also indexes # the images by various caption values. # # this program is called pcdindex because it was originally intended # for use on images that came from photocd, but they don't really have # to. the name stuck. # # PROGRAM DESIGN - wmw 19980410 (see, ten years after i started learning # how to program, i now know to explicate # "program design". thanks, yourdon.) # # all pcd's sit under a root pcd directory. # the root pcd is, say, ~/html/photography/pcd/ # and the directory containing the images is, say, ~/html/photography/pcd/mypix # # inside each pcd directory you will find a bunch of files, each with only # three identifying characteristics: each file has an image identifier coupled # with a size identifier and a format identifier, # for example: img0001-s.jpg, img0002-m.jpg, img0002-l.fpx # (my 'rename' utility will help with this a lot.) # if a certain image has three representations, -s.jpg, -m.jpg, and -l.fpx, # you MUST name the image the same way, eg IMG0023-s.jpg, IMG0023-m.jpg, IMG0023-l.fpx, # or this program won't see the connection. it's not THAT smart. # # by "small" i mean thumbnail-sized, eg. 100 pixels tall. # by "medium" i mean full-screen-sized, eg. 500 pixels tall. # by "large" i mean as big as you wanna get, for printing purposes, eg. 2000x3000. # # this program will create: # an index page showing all thumbnails for a given pcd directory # caption index pages showing full text info for each picture. # individual slide pages that show the medium size within the context # of the rest of the pcd, and that link to the large size. # individual slide pages that show the medium size with no reference # to the rest of the pcd, and that link to the large size. (not yet ready) # # a caption file should be located in the pcd directory; it should # be named captions.txt. the format is pretty obvious; look at any # existing captions.txt file and just follow it. # # each caption_line is defined in pseudo-bnf notation thusly: # caption_line: blank | [imagename] definition_line # definition_line: text | fieldname: text # imagename: "default" | non-whitespace containing at least two consecutive digits [:] # # if fieldname is listed in @autocomplete, the first word in the fieldname acts # as an abbreviation for the rest of the field, and will be expanded later in the # file when used alone. this is convenient for lens, etc definitions. # # if no fieldname is specified, the text is appended to the current field. # the default current field for any imagename is "title". this is a hardcoding in config above. # # mengwong 20040515 added EXIF support using metacam # ###################################################################### # -------------------------------------------------------------------- # variable initialization # -------------------------------------------------------------------- use vars qw($mason); @displayable_formats = qw(gif jpg jpeg); $displayable_formats = join("|", @displayable_formats); @flashpix_extensions = qw(fpx); $flashpix_extensions = join("|", @flashpix_extensions); $suffix_for{'small'} = "-s"; $suffix_for{'medium'} = "-m"; $suffix_for{'large'} = "-l"; $article_in = "article"; $captions_in = "captions.txt"; $medium_index = "bogus.html"; # this file is used solely to trick wwwis into telling us the sizes of the medium images. $map_fraction = 2; # the higher, the thinner the prev/next edges of the imagemap in the slide $text_color = "#ffffff"; $link_color = "#8888ff"; $vlink_color = "#ff8888"; $bgcolor = "#000000"; $chmod = "/bin/chmod"; my $metacam; # comment out the following line if metacam is not installed on this system. $metacam = "metacam"; $i_am_a_stud = < pcdindex by Rourke McNamara and Meng Weng Wong EOBRAG @flashpix_info_sources = qw( www.livepicture.com www.digitalimaging.org image.hp.com www.photo.net/photo/publishing-flashpix.html ); @flashpix_info_sources = map ("$_", @flashpix_info_sources); $more_info_about_flashpix = "
For more information about FlashPix, see one of
" . join(", ", @flashpix_info_sources) . ".\n"; @known_caption_fieldnames = qw( pcdtitle date lOCATION FILM CAMERA LENS FILTER EXPOSURE tITLE qUIP sERIES captionby Photographer Owner copyrightyear comment Url Urltext OCCASION ); # uppercasing turns on the autocomplete feature # firstupcasing means "do not html encode this field's contents" # firstdowncasing means "create caption index sort on this field" for (grep(lcfirst(uc($_)) eq $_, @known_caption_fieldnames)) { $caption_sorts{lc($_)} = 1; } for (grep(uc ($_) eq $_, @known_caption_fieldnames)) { $autocomplete_feature{lc($_)} = 1; } for (grep(ucfirst($_) eq $_, @known_caption_fieldnames)) { $do_not_htmlencode{lc($_)} = 1; } $known_caption_fieldnames = lc(join("|", @known_caption_fieldnames)); @caption_sorts = keys %caption_sorts; $default_current_field = "title"; # -------------------------------------------------------------------- # main program # -------------------------------------------------------------------- ; $| = 1; @alldirs = @ARGV; if (! @alldirs) { die ("usage: pcdindex pcd_directory [...]\n"); } # and we index each directory foreach $pcd (@alldirs) { $pcd =~ s/\/$//; # remove trailing slashe print "pcdindex: indexing $pcd ... "; # take a good look at the directory opendir(DIR, "$pcd") or die "unable to open dir $pcd: $!"; @dircontents = readdir(DIR); closedir DIR; @allsmalls = sort grep(/$suffix_for{'small'}\.($displayable_formats)$/i, @dircontents); @allmediums = sort grep(/$suffix_for{'medium'}\.($displayable_formats)$/i, @dircontents); @alllarges = sort grep(/$suffix_for{'large'}\./i, @dircontents); @subdirs = sort { lc($a) cmp lc($b) } grep(-d "$pcd/$_", (grep (! /^\.+$/, @dircontents))); undef %index; for (@allsmalls) { ($imagename) = /^(.*)$suffix_for{'small'}\.($displayable_formats)/i; $index{$imagename}{s} = $_; } for (@allmediums) { ($imagename) = /^(.*)$suffix_for{'medium'}\.($displayable_formats)/i; $index{$imagename}{m} = $_; } for (@alllarges) { ($imagename) = /^(.*)$suffix_for{'large'}\./i; $index{$imagename}{l} = $_; } @allimages = sort keys %index; # ------------------------------------------------------------ # read captions from file. # ------------------------------------------------------------ &read_caption_file; # ------------------------------------------------------------ # read article from file # ------------------------------------------------------------ $article = &read_article_file; # ------------------------------------------------------------ # discover exif data # ------------------------------------------------------------ my $do_exif = defined $metacam; my %exif_data; if ($do_exif and @alllarges) { my $jpg = ($alllarges[0] =~ /jpg/ ? "jpg" : $alllarges[0] =~ /JPG/ ? "JPG" : ""); print "reading EXIF... "; if ($jpg) { open (METACAM, "metacam $pcd/*-l.$jpg 2>/dev/null |"); my $filename; my $imagename; while () { chomp; if (/^File:.*\/(\S+)-l\.jpg$/i) { $imagename = $1; next; } if (/-----------------$/) { next; } s/^\s+//; my ($lhs, $rhs) = split /: /; next if not $lhs; $exif_data{$imagename}->{$lhs} = $rhs; # print "learned EXIF data: $imagename $lhs = $rhs\n"; } } } # File: /home/mengwong/html/mason/www.mengwong.com/photography/20040407-genos/dsc_3383-l.jpg # Standard Fields ----------------------------------- # Image Creation Date: 2004:04:07 01:58:35 # Make: NIKON CORPORATION # Model: NIKON D70 # Software Version: QuickTime 6.5 # X Resolution: 300 Pixels/Inch # Y Resolution: 300 Pixels/Inch # YCbCr Positioning: Datum Point # EXIF Fields --------------------------------------- # Sub-Second Creation Time: 80 # Image Capture Date: 2004:04:06 23:22:15 # Sub-Second Capture Time: 80 # Image Digitized Date: 2004:04:06 23:22:15 # Sub-Second Digitized Time: 80 # Exposure Bias: 0 EV # Focal Length: 22mm # Exposure Time: 1/15 Sec. # Aperture: f3.8 # Exif Image Width: 1504 pixels # Exif Image Height: 1000 pixels # Exposure Program: Program Normal # Exposure Mode: Auto Exposure # White Balance: Auto White Balance # Metering Mode: Multi-Segment/Pattern # EXIF Version: 0220 # FlashPix Version: 0100 # Light Source/White Balance: Automatic # Flash: Flash Not Fired; # Sensing Method: Single Chip Color Area Sensor # Compressed Bits Per Pixel: 4 # Max Aperture Value: f3.6 # ColorSpace: sRGB # Component Configuration: YCbCr # Digital Zoom Ratio: 1x # 35mm Focal Length: 33mm # Scene Capture Type: Standard # Contrast: Normal # Saturation: Normal # Sharpness: Normal # Subject Distance Range: Unknown # Manufacturer Fields ------------------------------- # Nikon Version Number: 0210 # ISO Equivalent: 1600 (0) # File Format: FINE # White Balance: AUTO # Sharpening: AUTO # Focus: AF-S # Flash Mode: NORMAL # Flash Metering mode: # White Balance Adjustment: 0 # Image Adjustment: AUTO # Lens: 18-70mm f3.5-f4.5 # Focus Position: Center # Flash Type: NATURAL # Noise Reduction: OFF # # File: /home/mengwong/html/mason/www.mengwong.com/photography/20040407-genos/dsc_3389-l.jpg # Standard Fields ----------------------------------- # Image Creation Date: 2004:04:07 02:00:32 # Make: NIKON CORPORATION # Model: NIKON D70 # Software Version: QuickTime 6.5 # X Resolution: 300 Pixels/Inch # Y Resolution: 300 Pixels/Inch # YCbCr Positioning: Datum Point # EXIF Fields --------------------------------------- # Sub-Second Creation Time: 00 # Image Capture Date: 2004:04:07 00:06:41 # Sub-Second Capture Time: 00 # Image Digitized Date: 2004:04:07 00:06:41 # Sub-Second Digitized Time: 00 # Exposure Bias: 0 EV # Focal Length: 18mm # Exposure Time: 1/60 Sec. # Aperture: f6.3 # Exif Image Width: 2240 pixels # Exif Image Height: 1488 pixels # Exposure Program: Program Normal # Exposure Mode: Auto Exposure # White Balance: Auto White Balance # Metering Mode: Multi-Segment/Pattern # EXIF Version: 0220 # FlashPix Version: 0100 # Light Source/White Balance: Automatic # Flash: Flash Not Fired; # Sensing Method: Single Chip Color Area Sensor # Compressed Bits Per Pixel: 4 # Max Aperture Value: f3.4 # ColorSpace: sRGB # Component Configuration: YCbCr # Digital Zoom Ratio: 1x # 35mm Focal Length: 27mm # Scene Capture Type: Standard # Contrast: Soft # Saturation: Normal # Sharpness: Normal # Subject Distance Range: Unknown # Manufacturer Fields ------------------------------- # Nikon Version Number: 0210 # ISO Equivalent: 400 (0) # File Format: FINE # White Balance: AUTO # Sharpening: AUTO # Focus: AF-S # Flash Mode: NORMAL # Flash Metering mode: # White Balance Adjustment: 0 # Image Adjustment: AUTO # Lens: 18-70mm f3.5-f4.5 # Focus Position: Center # Flash Type: COLORED # Noise Reduction: OFF # # File: /home/mengwong/html/mason/www.mengwong.com/photography/20040407-genos/dsc_3391-l.jpg # Standard Fields ----------------------------------- # Image Creation Date: 2004:04:07 00:20:31 # Make: NIKON CORPORATION # Model: NIKON D70 # Software Version: Ver.1.00 # X Resolution: 300 Pixels/Inch # Y Resolution: 300 Pixels/Inch # YCbCr Positioning: Datum Point # EXIF Fields --------------------------------------- # Sub-Second Creation Time: 20 # Image Capture Date: 2004:04:07 00:20:31 # Sub-Second Capture Time: 20 # Image Digitized Date: 2004:04:07 00:20:31 # Sub-Second Digitized Time: 20 # Exposure Bias: 0 EV # Focal Length: 25mm # Exposure Time: 1/30 Sec. # Aperture: f3.8 # Exif Image Width: 2240 pixels # Exif Image Height: 1488 pixels # Exposure Program: Program Normal # Exposure Mode: Auto Exposure # White Balance: Auto White Balance # Metering Mode: Multi-Segment/Pattern # EXIF Version: 0221 # FlashPix Version: 0100 # Light Source/White Balance: Automatic # Flash: Flash Not Fired; # Sensing Method: Single Chip Color Area Sensor # Compressed Bits Per Pixel: 4 # Max Aperture Value: f3.7 # ColorSpace: sRGB # Component Configuration: YCbCr # Digital Zoom Ratio: 1x # 35mm Focal Length: 37mm # Scene Capture Type: Standard # Gain Control: None # Contrast: Soft # Saturation: Normal # Sharpness: Normal # Subject Distance Range: Unknown # Manufacturer Fields ------------------------------- # Nikon Version Number: 0210 # ISO Equivalent: 400 (0) # File Format: FINE # White Balance: AUTO # Sharpening: AUTO # Focus: AF-S # Flash Mode: NORMAL # Flash Metering mode: # White Balance Adjustment: 0 # Image Adjustment: AUTO # Lens: 18-70mm f3.5-f4.5 # Focus Position: Center # Flash Type: NORMAL # Noise Reduction: OFF # # # # ------------------------------------------------------------ # write to indexfile. # ------------------------------------------------------------ open (INDEXFILE, ">$pcd/index.html") or die "couldn't write to $pcd/index.html: $!"; print INDEXFILE <Thumbnail Index of $pcd

Index of $pcd (back up)

$article EOHEAD if (defined(%caption)) { @also_indexed_by = @caption_sorts; } for (@caption_sorts) { print "%{\$captions_by{$_}} has @{[scalar keys%{$captions_by{$_}}]} elements.\n" if $debug; } @also_indexed_by = grep(keys (%{$captions_by{$_}}) > 0, @caption_sorts); if (@also_indexed_by) { print INDEXFILE "Also indexed by "; print INDEXFILE join(", ", map ("$_", @also_indexed_by[0..$#also_indexed_by-1])); print INDEXFILE (@also_indexed_by > 1 ? " and " : ""); print INDEXFILE map ("$_", $also_indexed_by[-1]); print INDEXFILE ".

\n"; } if (@subdirs) { print INDEXFILE "Subdirectories: "; print INDEXFILE join(", ", map ("$_", @subdirs)); print INDEXFILE ".

\n"; } foreach $imagecount (0 .. $#allimages) { # add an entry to the indexfile. $imagename = $allimages[$imagecount]; # if there is a small but no medium and no large, thumbnail is not clickable. # if there is no small but there is a medium or large, thumbnail is text. # the normal case, of course, is to create a thumbnail image that links to the slide. $altfor = &altfor($imagename); $small_imgsrc = "\"$altfor\""; if ($index{$imagename}{s} and (! $index{$imagename}{m}) and (! $index{$imagename}{l})) { print INDEXFILE "$small_imgsrc \n"; } elsif ((! $index{$imagename}{s}) and ($index{$imagename}{m} or $index{$imagename}{l})) { print INDEXFILE "[$imagename]\n"; } else { print INDEXFILE "$small_imgsrc\n"; } } close INDEXFILE; # ------------------------------------------------------------ # new work by wmw 19980417 # create a bogus bigindex.html file containing all the -m's. # call wwwis on it. # learn about the height and width tags thusly generated. # build in height and width tags into the slide pages # build in imagemap ability into the slide pages # ------------------------------------------------------------ open (MEDIUMINDEX, ">$pcd/$medium_index"); foreach $imagecount (0 .. $#allimages) { $imagename = $allimages[$imagecount]; if (defined($index{$imagename}{m})) { print MEDIUMINDEX "\n"; } } close MEDIUMINDEX; $wwwis_out = `wwwis $pcd/$medium_index; rm $pcd/*~`; undef %width; undef %height; open (MEDIUMINDEX, "$pcd/$medium_index"); while () { ($imagename, $width, $height) = /imagename=(\S+).*width=(\d+)\s*height=(\d+)/i; $width{$imagename} = $width; $height{$imagename} = $height; # print "learned $imagename is $width by $height\n"; } close MEDIUMINDEX; unlink ("$pcd/$medium_index", "$pcd/$medium_index~"); # ------------------------------------------------------------ # dump output to slide page # ------------------------------------------------------------ foreach $imagecount (0 .. $#allimages) { $imagename = $allimages[$imagecount]; open(SLIDE, ">$pcd/$imagename.html") or die "unable to write to $imagename.html: $!\n"; $htmltitle = &index_text_no_html($imagename); $map = ""; $usemap = ""; if (defined($width{$imagename})) { $height = $height{$imagename}; $width = $width{$imagename}; $mapl = int($width / $map_fraction); $mapr = ($map_fraction - 1) * $mapl; $previmage = $allimages[($imagecount - 1) % @allimages] . ".html"; $nextimage = $allimages[($imagecount + 1) % @allimages] . ".html"; $previmagealt=&altfor($allimages[($imagecount - 1) % @allimages]); $nextimagealt=&altfor($allimages[($imagecount + 1) % @allimages]); $previmagealt="previous image" . ($previmagealt ? ": $previmagealt" : ""); $nextimagealt="next image" . ($nextimagealt ? ": $nextimagealt" : ""); $usemap=" USEMAP=\"#map\""; $map = <<"EOMAP"; $previmagealt $nextimagealt EOMAP } $lowsrc = ""; if ($index{$imagename}{s}) { $lowsrc = " LOWSRC=\"$index{$imagename}{s}\""; } $altfor = &altfor($imagename); if (! $index{$imagename}{m}) { $medium_image = "[sorry, no medium-sized image available.]"; } else { $medium_image = "\"$altfor\"$lowsrc$usemap"; } $caption = &formulate_caption($imagename); if ($index{$imagename}{l}) { $in_flashpix = ""; $in_flashpix = " in FlashPix" if ($index{$imagename}{l} =~ /($flashpix_extensions)/); $large_available = "A huge version$in_flashpix is available."; $large_available .= $more_info_about_flashpix if ($in_flashpix); $large_availabel .= "

"; } else { undef $large_available; } if (-e "$pcd/$imagename.pcd") { $pcd_available = "Apparently, the original photo cd scan is also available.

"; } else { undef $pcd_available; } my $masonstuff = ""; if ($mason) { $masonstuff = <<'SINGLEQUOTED' . << "DOUBLEQUOTED"; % my @dhandler; my $fulldir; % # manually detect dhandler % foreach my $dir (split /\//, $m->current_comp->dir_path) { next if not length $dir; $fulldir .= "/$dir"; carp ("fulldir is $fulldir") if 0; push @dhandler, "$fulldir/dhandler" if $m->comp_exists("$fulldir/dhandler"); } % # carp ("dir_path is " . $m->current_comp->dir_path() . "; dhandler is @dhandler"); % if (@dhandler) { <& $dhandler[-1], 'caller' => $m->current_comp &> % } SINGLEQUOTED <%attr> small => "$index{$imagename}{s}" medium => "$index{$imagename}{m}" large => "$index{$imagename}{l}" title => q($caption{$imagename}{title}) DOUBLEQUOTED } my $exif_data; if ($exif_data{$imagename}) { $exif_data = "

\n"; # standardize EXIF fieldnames. why couldn't the EXIF format have defined proper names? for ($exif_data{$imagename}) { $_->{"Aperture"} = delete $_->{"Aperture Value"} if exists $_->{"Aperture Value"}; $_->{"Exposure Time"} = delete $_->{"Shutter Speed Value"} if exists $_->{"Shutter Speed Value"}; $_->{"ISO Equivalent"} = delete $_->{"ISO Speed Rating"} if exists $_->{"ISO Speed Rating"}; delete $_->{"Focal Length"} if exists $_->{"35mm Focal Length"}; } foreach my $key_set ([ "Exposure Time", "Aperture", "ISO Equivalent", "Exposure Bias", ], [ "Lens", "Focal Length", "35mm Focal Length", "Exposure Program", "Model", ] ) { $exif_data .= "\n"; } $exif_data.="
\n"; foreach my $exif_key (@$key_set) { next if not my $value = $exif_data{$imagename}{$exif_key}; next if $exif_key eq "Exposure Bias" and $value eq "0 EV"; $exif_data .= ""; } $exif_data .= "
$exif_key$value

\n"; } else { $exif_data = ""; } print SLIDE <$htmltitle $map
$medium_image

@{[ &skip($imagecount, -1, "showthumb") ]}
@{[ &skip($imagecount, -5) ]}
@{[ &skip($imagecount,-10) ]}
@{[ &skip($imagecount,-20) ]}
$caption $exif_data

$large_available $pcd_available $masonstuff

@{[ &skip($imagecount, +1, "showthumb") ]}
@{[ &skip($imagecount, +5) ]}
@{[ &skip($imagecount,+10) ]}
@{[ &skip($imagecount,+20) ]}

$i_am_a_stud
EOSLIDE close SLIDE; } # -------------------------------------------------------------------- # create caption files # -------------------------------------------------------------------- # @captions_by{series}{trip to hong kong} = (img0001, img0002, ...) if (! defined(%caption)) { print "no captions ... "; } else { print "creating caption indexes ... "; foreach $type (@caption_sorts) { open (OUT, ">$pcd/index-$type.html"); print OUT <<"EOTOP"; Images in $pcd, by $type

Images in $pcd, by $type

EOTOP ; foreach $value (sort mysort keys %{$captions_by{$type}}) { if ($value eq "") { $captions_by{$type}{"(no $type)"} = $captions_by{$type}{""}; delete ($captions_by{$type}{""}); } } if ($type eq "series" or $type eq "location") { foreach $value (sort mysort keys %{$captions_by{$type}}) { next if ($value eq "__all__"); print OUT "

$value

\n\n
    \n"; &list_images(sort {lc(&index_text_no_html($a)) cmp lc(&index_text_no_html($b))} @{$captions_by{$type}{$value}}); print OUT "
\n\n"; } } elsif ($type eq "title") { &list_images(sort {lc(&index_text_no_html($a)) cmp lc(&index_text_no_html($b))} @{$captions_by{$type}{"__all__"}}); } else { foreach $value (sort mysort keys %{$captions_by{$type}}) { next if ($value eq "__all__"); print OUT "

$value

\n\n
    \n"; @images_to_list = sort {lc(&index_text_no_html($a)) cmp lc(&index_text_no_html($b))} @{$captions_by{$type}{$value}}; &list_images(@images_to_list); print OUT "
\n\n"; } } print OUT "\n"; close OUT; } } # -------------------------------------------------------------------- # finish up # -------------------------------------------------------------------- print "chmod... "; system("$chmod -R a+rX $pcd"); print "wwwis... "; system("wwwis $pcd/*.html > /dev/null"); system("rm $pcd/*~"); print "done.\n"; } # -------------------------------------------------------------------- # chunks of text # -------------------------------------------------------------------- # -------------------------------------------------------------------- # functions # -------------------------------------------------------------------- sub mysort { # sole purpose is for the (no $type) sections to go last on the captions-index page. return $a cmp $b if ($a !~ /^\w/ and $b !~ /^\w/); return 1 if ($a !~ /^\w/ and $b =~ /^\w/); return -1 if ($a =~ /^\w/ and $b !~ /^\w/); return lc($a) cmp lc($b); } sub index_text { local ($imagename) = @_; local ($index_text); return undef if (! defined($caption{$imagename})); $index_text = "$caption{$imagename}{quip}: " if (($type ne "quip") and length($caption{$imagename}{quip})); $index_text .= $caption{$imagename}{title}; # $index_text .= " ($caption{$imagename}{comment})" if (length($caption{$imagename}{comment})); $index_text ||= "$imagename (untitled)"; return $index_text; } sub index_text_no_html { # for sorting purposes local ($imagename) = @_; local ($index_text); $index_text = &index_text($imagename); $index_text =~ s/<[^>]*>//g; return $index_text; } sub list_images { local (@imagenames) = @_; foreach $imagename (@imagenames) { print OUT "
  • "; print OUT &index_text($imagename); print OUT "\n"; } } sub skip { local ($imagecount, $distance, $showthumb) = @_; local ($target) = ($imagecount + $distance) % @allimages; local ($targettext) = ($distance < 0 ? $distance : "+$distance" ); $targettext = ($distance > 0 ? "next" : "previous" ) if (abs($distance) == 1); $target = sprintf("%03d", $target); if ($showthumb and defined($index{$allimages[$target]}{s})) { $altfor = &altfor($allimages[$target]); $targettext = "\"$altfor\""; # print "targettext = $targettext\n"; } return "$targettext"; } sub formulate_caption { # build $caption for individual instance. local ($toreturn, @hardware, @toreturn); local ($imagename) = lc($_[0]); if (! defined($caption{$imagename})) { return "$imagename from $pcd"; } if (defined($caption{$imagename}{quip})) { push (@toreturn,"\"$caption{$imagename}{quip}\""); } if (defined($caption{$imagename}{title})) { push (@toreturn,"$caption{$imagename}{title}"); } else { push (@toreturn, "$imagename from $pcd"); } if (defined($caption{$imagename}{location})) { push (@toreturn,"$caption{$imagename}{location}"); } if (defined($caption{$imagename}{date})) { $toreturn[-1] .=", $caption{$imagename}{date}"; } if (defined($caption{$imagename}{occasion})) { push (@toreturn,"occasion: $caption{$imagename}{occasion}"); } if (defined($caption{$imagename}{comment})) { push (@toreturn,"$caption{$imagename}{comment}"); } if (defined($caption{$imagename}{url})) { push (@toreturn,"$caption{$imagename}{urltext}"); } if (defined($caption{$imagename}{copyrightyear}) and defined($caption{$imagename}{owner}) and ($caption{$imagename}{owner} eq "photographer")) { push (@toreturn,"copyright © $caption{$imagename}{copyrightyear} by $caption{$imagename}{photographer}"); } else { push (@toreturn,"created by $caption{$imagename}{photographer}") if (defined($caption{$imagename}{photographer})); push (@toreturn,"copyright © $caption{$imagename}{copyrightyear} by $caption{$imagename}{owner}") if (defined($caption{$imagename}{owner}) and defined($caption{$imagename}{copyrightyear})); } if (defined($caption{$imagename}{series})) { $seriesname = "$caption{$imagename}{series}"; if (defined($caption_sorts{series})) { $seriesname = "$seriesname\n"; } push (@toreturn,"$imagename from the series \"$seriesname\"" . (defined($caption{$imagename}{pcdtitle}) ? " collected on $caption{$imagename}{pcdtitle}" :""));} if (defined($caption{$imagename}{film})) { push (@hardware, $caption{$imagename}{film}); } if (defined($caption{$imagename}{camera})) { push (@hardware, $caption{$imagename}{camera}); } if (defined($caption{$imagename}{lens})) { push (@hardware, $caption{$imagename}{lens}); } if (defined($caption{$imagename}{filter})) { push (@hardware, $caption{$imagename}{filter}); } if (@hardware) { push (@toreturn, join(", ", @hardware)); } if (defined($caption{$imagename}{exposure})) { push (@toreturn,"exposure: $caption{$imagename}{exposure}"); } if (defined($caption{$imagename}{captionby})) { push (@toreturn,"\n"); } $toreturn = join("
    \n", @toreturn) . "\n"; return $toreturn; } sub read_article_file { local ($articlefile, $article); foreach $ext ("html", "shtml", "txt") { if (-e "$pcd/$article_in.$ext") { $articlefile = "$pcd/$article_in.$ext"; last; } if (! $articlefile) { return; } } open (ARTICLEFILE, $articlefile); $article = join("", ); close ARTICLEFILE; if ($articlefile =~ /txt$/) { $article = "
    $article
    "; } $article .= "

    "; return $article; } sub read_caption_file { # build %caption undef %caption; undef %autocomplete; undef %seenbefore; undef $series_id; undef %series_id; undef $location_id; undef %location_id; if (! -e "$pcd/$captions_in") { return; } open (CAPTIONFILE, "$pcd/$captions_in") or warn "pcdindex: unable to open caption file $pcd/$captions_in: $!\n" and return; @captionfile = (); close CAPTIONFILE; for (@captionfile) { chop; $captionline++; s/^\s+|\s+$//g; next if (/^$/ or /^\s*\#/); if (s/^\s*(:\S+:|default)\s*//i) { # got an imagename as leading token $imagename = lc($1); $imagename =~ s/^:|:$//g; if ($lastimagename ne $imagename) { # dealing with a different imagename $current_field = $default_current_field; # dealing with an imagename for the first time if (! $seenbefore{$imagename}++) { for (keys %{$caption{default}}) { $caption{$imagename}{$_} = $caption{default}{$_}; } } } $lastimagename = $imagename; } if (s/^\s*($known_caption_fieldnames)\s*:\s*//i) { # got a fieldname as leading token $current_field = lc($1); delete $caption{$imagename}{$current_field}; } next if (! length($_)); if (! defined($imagename)) { warn "pcdindex: error reading captionfile: what image are we talking about at line $captionline?\n"; next; } if (! defined($current_field)) { warn "pcdindex: error reading captionfile: what field, exactly, are we defining at line $captionline?\n"; next; } if ($autocomplete_feature{$current_field}==1) { if (/^(\S+)(.*)$/) { $firstword = $1; if (length($2)) { $autocomplete{$current_field}{$firstword} = $_; } # learn else { $_ = $autocomplete{$current_field}{$firstword}; } # regurgitate # yup, i grew up # in singapore. } } $_ = &htmlencode($_) unless ($do_not_htmlencode{$current_field}); $caption{$imagename}{$current_field}.=" " if (exists($caption{$imagename}{$current_field})); $caption{$imagename}{$current_field}.=$_; print STDERR "learned $imagename\{$current_field\} = $caption{$imagename}{$current_field}\n" if ($debug); if (($current_field eq "url") and (! defined($caption{$imagename}{urltext}))) { $caption{$imagename}{urltext} = $caption{$imagename}{url}; } if (($current_field eq "series") and (! defined($series_id{$_}))) { $series_id{$_} = $series_id++; } if (($current_field eq "location") and (! defined($location_id{$_}))) { $location_id{$_} = $location_id++; } } # # build various sort indexes # undef %captions_by; foreach $imagename (sort keys %caption) { next if ($imagename eq "default"); next if (! defined($index{$imagename})); foreach $type (@caption_sorts) { next if (! defined($caption{$imagename}{$type})); push (@{$captions_by{$type}{$caption{$imagename}{$type}}}, $imagename); push (@{$captions_by{$type}{"__all__"}}, $imagename); print STDERR "push (\@{\$captions_by{$type}{$caption{$imagename}{$type}}}, $imagename)\n" if $debug; } } } sub htmlencode { local ($_) = @_; s/&/&/g; s/>/>/g; s/