#!/usr/local/bin/perl # This is a program for batch-converting PhotoCDs to JPEG # It was written by a friend of Philip Greenspun's who wishes # to remain anonymous. She is licensing her code, however, under # the standard GNU public license. # for more on how to use this program, visit http://demo.webho.com # calling this program # we assume that we are already CD'd into the target directory # e.g., ~loser/public_html/pcd1234/ # and that this directory already contains a spec file, probably # something like pcd1234-description.text, but this is an input # pcdconvertwholedisk.pl # this program is supposed to take an entire PhotoCD and convert it to # JPEGs. Also, it creates three index files. One contains anchors to # be used in-line in text. One contains one HTML table per image. # This is as close to having a captioned image as one can get in the # impoverished world of HTML. The final file is probably only of # interest to Philip Greenspun because it is for generating tutorials # in http://photo.net/photo/ (it displays information that can help # teach other photographers). # format for the description file # first part of the description file contains info about entire disk # then each line describes one image # line1: WHICH RESOLUTIONS BY DEFAULT # default_resolutions: <4base?><16base?><64base?> # where each is a 0 or 1 # example: pull base/16, base, and 4base out of each Image Pac: 101100 # (note: only ProPhotoCDs have 64base scans) # line2: COPYRIGHT SPEC # copyright: <6 char resolution spec> | # so to add a copyright note to base and 4base, the line would read # copyright: 001100 | "copyright 1997 Philip Greenspun" # we provide this flexibility because base/16 and base/4 images # are really too small for a visible copyright notice # notes: (1) the copyright text is added to JFIF files at all # resolutions as a comment but it won't be visible; (2) if the copyright # text is "" then no copyright info will be inserted # line3: BORDERS # we need to turn black borders on or off because scanned slides often # already have a border; we're going to assume that disk is either all # slides or prints and not make it adjustable per-image # border: yes OR border: no # line4: SHARPENING # sharpen: yes OR sharpen: no # line5: URL STUB # url_stub:whatever you want in front of the file names in IMGs and HREFs # each image to be converted gets one line, immediately following the above # |||||| # example: 58|alex||my baby|Canon EOS, 50mm lens, Kodak E100 film, f/5.6 and 1/125| # in this case, this software should pull Image Pac IMG0058.pcd off the PhotoCD # WHICH_RESOLUTIONS wasn't specified so we use the default for this PCD. Suppose # it is 101100. In that case, the base/16 image is converted and saved as # alex-58.1.jpg (note that the image number was tacked onto the file name and that the # resolution is added). The base image is converted and saved as alex-58.3.jpg. # The 4base image is converted and saved as alex-58.4.jpg. # HTML anchors are created in the index.html file: # $alt # BIG # note: if the ALT is unspecified in the conversion line then we just # use $caption # an HTML table is added created to the index-table.html file: #
align=center> #$alt #
#BIG #
# $caption #
# an HTML table is added created to the index-tutorial.html file: #
align=center> #$alt #
#BIG #
# $caption #

# $tech_details #

# $tutorial_info #

# notes on which files get linked to which others... # typically base/16 and base are converted and the first becomes an IMG # anchor for the second. # if the base/4 image (256x384) and 4base (1000x1500) are converted then # the first becomes an IMG anchor for the second. # if both of these pairs are converted, then the index.html et al files # should contain two entries for each picture. #TO DO: add a -indexonly option for the slavedriver # clean up and comment # check both the copyright_res string && whether the copyright text is blank #before a -draw option is issued. #move files to *.bak rather than dying if they already exist- print warning #need to do this for convert-cmds #pcdconvertwholedisk #usage: pcdconvertwholedisk {-indexonly} ######################PARAMETERS############################################# #FILE LOCATIONS $exec_cmd = '/usr/local/bin/convert'; #the exec name for ImageMagick #Image Pac location; only the lowercase portion varies $pcd_image_directory = '/cdrom/PHOTO_CD/IMAGES/'; # set this to 1 if filenames look like "IMG0023.PCD;1" $pcd_image_filenames_contain_semicolon_p = 1; #IMAGE MAGICK DEFAULTS $x_display = "localhost:0"; @interlaces = ('NONE', 'NONE', 'NONE', 'NONE', 'NONE', 'NONE'); #default color for copyright string @pen_colors = ("white", "white", "white", "white", "white", "white"); #default vertical border widths for each of the 6 image sizes @x_borders = (2, 4, 8, 10, 10, 10); @y_borders = (3, 6, 12, 15, 15, 15); #default horizontal border widths #default border colors @border_colors = ("black", "black", "black", "black", "black", "black"); #difference between rightmost position of copyright string and rightmost #x coordinate of image (including vertical border @x_offsets = (5, 5, 5, 5, 5, 5); #difference between bottom of lower horizontal border and bottom of #copyright string @y_offsets = (2, 2, 2, 2, 2, 2); @sharpens = (50, 50, 50, 50, 50, 50); #default sharpen values #widths for horizontal images @image_widths = (128, 256, 512, 1024, 2048, 4096); #heights for horizontal images @image_heights = (192, 384, 768, 1536, 3072, 6144); @font_widths = (6, 6, 6, 8, 8, 8); @font_heights = (9, 9, 9, 13, 13, 13); #OUTPUT FILENAMES $index_filename = "index.html"; $table_filename = "index-table.html"; $tutorial_filename = "index-tutorial.html"; $convert_filename = "convert_commands"; #HTML HEADERS AND FOOTERS $html_header = ' PhotoCD Index

PhotoCD Index


'; $html_footer_1 = '
'; # the copyright string from the description file will be sandwiched in between $html_footer_2 = ' '; #DEFAULT RESOLUTIONS for Img src, big, huge $img = 1; $big = 3; $huge = 4; ########################END PARAMETERS####################################### #check to ensure that only one description file has been specified, and #that it is readable $name = (shift(@ARGV) || &usage); #check to see if they are specifying the -indexonly option if ($name eq "-indexonly") { $INDEXONLY = 1; $name = (shift(@ARGV) || &usage); } (@ARGV == 0) || &usage; open(INFILE, $name) || die "Unable to open $name\n"; #parse the first five lines (default setup) $str = ; ($str = &res_check($str)) || die "Bad default resolution format: $str"; $default_resolutions = $str; ($str, $copyright_text) = split(/\|/, ); ($str = &res_check($str))|| die "Bad copyright resolution format: $str"; $copyright_resolutions = $str; chop($copyright_text); ($borderfront, $borderback) = split(/:/, ); ($_ = &yes_no_check($borderback)) || die "Border default not correctly specified: $_"; $border = $_; ($sharpfront, $sharpback) = split(/:/, ); ($_ = &yes_no_check($sharpback)) || die "Sharpening default not correctly specified: $_"; $sharpen = $_; #get the directory for the jpeg file location-- used only in the #index files ($jpeg_image_dir_front, $jpeg_image_dir) = split(/:/, ); $jpeg_image_dir =~ s/\s//g; #open handles to the three output files if(-e $index_filename) { print "moving $index_filename to $index_filename.bak\n"; rename ($index_filename, "$index_filename.bak") || die "unable to rename $index_filename";} open(INDEX, ">$index_filename") || die "cannot create $index_filename"; if(-e $table_filename) { print "moving $table_filename to $table_filename.bak\n"; rename ($table_filename, "$table_filename.bak") ||die "unable to rename $table_filename";} open(TABLE, ">$table_filename") || die "cannot create $table_filename"; if(-e $tutorial_filename) { print "moving $tutorial_filename to $tutorial_filename.bak\n"; rename ($tutorial_filename, "$tutorial_filename.bak") ||die "unable to rename $tutorial_filename";} open(TUTORIAL,">$tutorial_filename") ||die "cannot create $tutorial_filename"; open(CONVERT, ">$convert_filename") || die "cannot create $convert_filename"; # add html headers print INDEX $html_header; print TABLE $html_header; print TUTORIAL $html_header; #get indications for each individual image while($_ = ) { #parse the line and do a cursory check on format $_ =~ /^\s*$/ && next; #if it's a blank line, skip it # chop off the trailing carriage return (only if it exists) $temp = $_; if ( chop($temp) eq "\n" ) { $_ = $temp; } ($number_on_disk, $target_file_name, $which_resolutions, $caption, $tech_details, $alt, $tutorial_info) = split(/\|/); # kill off white space in which_resolutions $which_resolutions =~ s/\s//g; if ($which_resolutions) { #if they specified a new resolution, check it &res_check($which_resolutions) || die "Bad resolution in image format: $_"; } else { $which_resolutions = $default_resolutions; #otherwise use default } #if they don't specify alt, use caption $alt || ($alt = $caption); #put the image res characters into an array to make looping easier @image_resolutions = split(//,$which_resolutions); @copyright_resolutions = split(//, $copyright_resolutions); #find the orientation for this image- since it doesn't depend on #resolution, use the first size $imagefile = &create_pcd_image_filename($number_on_disk, 1); $horizontal_p = &horizontal_p($imagefile); #jpegfront is withouta resolution specification. we tack that #on while iterating through the desired resolutions #this makes it slightly more efficient to create the html files $jpegfront = &create_jpeg_front($number_on_disk, $target_file_name); #reset some arrays @heights = @widths = (); #cycle through the desired resolutions to convert for ($i=0; $i<=5; $i++){ #calculate height & width ( for the HEIGHT & WIDTH tag values #and for the position parameter calculation) #need to do this for all files, not just extracted ones #so that HEIGHT & WIDTH tags can be calculated #this needs to be calculated once per image because the #values will change depending on the image orientaton. #should consider calculating a "horizontal height/width" #and a "vertical heght/width" array outside the image #description processing loop #first, check to see if borders are turned off. if so, #set all border sizes to 0 if ($border eq "NO"){ @y_borders = @x_borders = ();} if ($horizontal_p) { #it's horizontal $heights[$i] = ($image_heights[$i] + (2 * $y_borders[$i])); $widths[$i] = ($image_widths[$i] + (2 * $x_borders[$i])); } else { #it's vertical $heights[$i] = ($image_widths[$i] + (2 * $y_borders[$i])); $widths[$i] = ($image_heights[$i] + (2 * $x_borders[$i])); } if ($image_resolutions[$i]){ #if they want to pull this size #create strings for images $imagefile = &create_pcd_image_filename($number_on_disk, $i); $jpegfile = &create_jpeg_filename($jpegfront, $i); #calculate the position parameters for draw $sizeof_copyright = length($copyright_text); $draw_height = $heights[$i] - $y_offsets[$i] - $font_heights[$i]; $draw_width = $widths[$i] - $x_offsets[$i] - ($font_widths[$i] * $sizeof_copyright); @full_exec_cmd = &create_full_exec_cmd($i); unless ($INDEXONLY) { #call ImageMagick to convert images print "@full_exec_cmd\n"; system(@full_exec_cmd) && die "Unable to execute: $!"; } #write the commands to a text file that can be src'd print CONVERT "@full_exec_cmd\n"; } } #Now that we're done with a line, create/add to the index files. Here #we assume that all files will follow the $img $big $huge format (usually #1, 3, 4 as resolutions #Note: this is how the index is created even if one or more of the {1,3,4} #resolutions are not extracted from the PCD #create/add to index.html $index_entry = &create_index_entry($jpegfront); print INDEX $index_entry; #create/add to index-table.html $table_entry = &create_table_entry($jpegfront); print TABLE $table_entry; #create/add-to index-tutorial.html $tutorial_entry = &create_tutorial_entry($jpegfront); print TUTORIAL $tutorial_entry; } #add html footers & copyright print INDEX $html_footer_1 . $copyright_text . $html_footer_2; print TABLE $html_footer_1 . $copyright_text . $html_footer_2; print TUTORIAL $html_footer_1 . $copyright_text . $html_footer_2; #close up all the files close(INFILE); close(INDEX); close(TABLE); close(TUTORIAL); close(CONVERT); #SUBS sub usage { die "Usage: $0 {-indexonly} \n"; } sub res_check { local($_) = @_; s/\s//g; #strip off whitespace /[01]{6}/ || ($_ = ""); #make sure format is 6 chars from (0,1) return $_; } sub yes_no_check { local($_) = @_; s/\s//g; #strip whitespace tr/a-z/A-Z/; #translate to all uppercase (/YES/ || /NO/) || ($_ = ""); #make sure flag is either YES or NO return $_; } # is the image horizontal? If so, return 1 sub horizontal_p { local($name, $buf, $check1, $check2, $rotate, $trotate, $retval); $name = $_[0]; open (PCDFIL, $name) || die ("$name: $!\n"); read(PCDFIL, $buf, 3*0x800, 0); $check1 = substr($buf, 0, 7); $check2 = substr($buf, 0x800, 3); if (($check1 ne "PCD_OPA") && ($check2 ne "PCD")) { die "$name: not a PCD image file"; } $rotate = substr($buf, 0x0e02, 1); $trotate = (0x0003 & ord($rotate)); $retval = ($trotate == 1 || $trotate == 3) ? 1 : 0; close(PCDFIL); return $retval; } sub create_pcd_image_filename{ local($num, $res) = @_; local($retname); # pad $num with leading zeros to 4-characters $num = sprintf("%4s", $num); $num =~ s/ /0/g; $retname = $pcd_image_directory . "IMG" .$num . '.PCD'; if ( $pcd_image_filenames_contain_semicolon_p ) { $retname .= ";1"; } return $retname; } sub create_jpeg_front{ local($num, $name) = @_; local($retname); if($name =~ /^\s*$/){ #if it's all whitespace $retname = $num; } else { $retname = $name . "-" . $num; } return $retname; } sub create_jpeg_filename{ local($name, $res) = @_; local($retname); #the indices run from 0-5, but we want the name from 1-6 $res++; $retname = $name . '.' . $res . '.' . "jpg"; return $retname; } sub create_tutorial_entry{ local($name) = @_; local($entry, $altstr); if ($alt){ $altstr = '" ALT="' . $alt . '"';} else{ $altstr = '"';} $entry = "
\n" . '' . "\n
\nBIG' . "\n
\n$caption\n

\n$tech_details\n

\n$tutorial_info\n

\n\n"; return $entry; } sub create_table_entry{ local($name) = @_; local($entry, $altstr); if ($alt){ $altstr = '" ALT="' . $alt . '"';} else{ $altstr = '"';} $entry = "
\n" . '' . "\n
\nBIG' . "\n
\n$caption\n
\n\n"; return $entry; } sub create_index_entry{ local($name) = @_; local($entry, $height, $width, $altstr); $height = $heights[($img - 1)]; $width = $widths[($img - 1)]; if ($alt){ $altstr = '" ALT="' . $alt . '"';} else{ $altstr = '"';} $entry = '' . "\nBIG' . "\n\n"; return $entry; } sub create_full_exec_cmd{ local($i) = @_; local(@retval, $interlace_cmd, $sharpen_cmd, $border_cmd, $bordercolor_cmd, $comment_cmd, $font_cmd, $pen_cmd, $draw_cmd, $display_cmd, $pcd_name, $jpeg_name); $interlace_cmd = " -interlace $interlaces[$i]"; if ($sharpen eq "YES"){ $sharpen_cmd = " -sharpen $sharpens[$i]";} if ($border eq "YES"){ $border_cmd = " -border $x_borders[$i]x$y_borders[$i]"; $bordercolor_cmd = " -bordercolor $border_colors[$i]";} $comment_cmd = " -comment \'$copyright_text\'"; if($copyright_resolutions[$i]){ $display_cmd = " -display $x_display"; $draw_cmd = " -draw \'text $draw_width,$draw_height \"$copyright_text\"'"; $font_cmd = " -font $font_widths[$i]x$font_heights[$i]"; $pen_cmd = " -pen $pen_colors[$i]"; } #we index from 0-5, but want the filename from 1-6 $i++; $pcd_name = " '$imagefile\[$i]'"; $jpeg_name = " $jpegfile"; @retval = ($exec_cmd . $interlace_cmd . $sharpen_cmd . $bordercolor_cmd . $border_cmd . $comment_cmd . $font_cmd . $display_cmd . $pen_cmd . $draw_cmd . $pcd_name . $jpeg_name); return @retval; }