and

tags should be indented. define ("CFG_INDEX_LINK_INDENT", 0); //For the created index file, the number of characters that the //tags should be indented. //-------------------------------------------------------------------------------- //Calculates important integer indices related to creating a thumbnail, in order //to minimize distortion in the thumbnail. // //When creating a thumbnail, the longer side may be relatively short (perhaps //100 pixels), and the shorter side may be even shorter (perhaps even as short //as 10 pixels). If the dimensions of the original image were not changed, //converting the image to a thumbnail may result in distortion of up to about //one percent. As an example, suppose that a 1600 x 900 image is converted to a //thumbnail with the longer side of 125 pixels. The ideal dimension for the //shorter side would be (900/1600) * 125 = 70.3125 pixels. The aspect ratio of //the thumbnail can't be made to match the aspect ratio of the original image. // //To avoid any aspect ratio distortion that might be noticeable, this program //chooses the shorter dimension for the thumbnail that is the ceiling of the actual //quotient, then crops the longer dimension of the original image before the //conversion to try and match the aspect ratios as closely as possible. // //In the example a paragraph or two above, the shorter side of the thumbnail //would be chosen to be 71 pixels. // //Once this choice is made, we want to trim the original image before conversion //so that n/900 is as close as possible to 125/71. n = (900 * 125) / 71 = //1584.5070 pixels, so we choose 1585. This keeps the aspect ratio of the //original image and the thumbnail as close as possible. // //Although the longer side of the original image may be reduced prior to the //conversion to a thumbnail, this information is not written to disk, and //the original image is not modified. The adjustment of the longer side is //just a conversion trick to hopefully get better thumbnails. function calc_thumbnail_conversion_pars ( $in_orig_longer, //Longer dimension of original image. $in_orig_shorter, //Shorter dimension of original image. $in_thumbnail_longer, //Longer dimension of desired thumbnail. & $out_thumbnail_longer, //Longer dimension of thumbnail that should be created. & $out_thumbnail_shorter, //Shorter dimension of thumbnail that should be created. & $out_orig_crop_dim_longer, //The size to crop to on the longer axis of the original. & $out_orig_crop_dim_shorter, //The size to crop to on the shorter axis of the original. & $out_orig_crop_start_longer, //For use with the Imagick:cropImage() method, the start //position of the crop on the longer axis. & $out_orig_crop_start_shorter //For use with the Imagick:cropImage() method, the start //position of the crop on the shorter axis. ) { //Set the thumbnail shorter dimension to the floor. This means would need to trim //longest dimension of original to match aspect ratio as closely as possible. $out_thumbnail_longer = $in_thumbnail_longer; $out_thumbnail_shorter = ceil(((float)$in_thumbnail_longer * (float)$in_orig_shorter) / (float)$in_orig_longer); settype($out_thumbnail_shorter, "integer"); //The aspect ratio of the thumbnail is now set. Try to match the aspect ratio of the larger //image as closely as possible by selecting a smaller number for the long axis of the //original image. $out_orig_crop_dim_longer = round(((float)$in_thumbnail_longer * (float)$in_orig_shorter) / (float)$out_thumbnail_shorter); settype($out_orig_crop_dim_longer, "integer"); //The original keeps its shorter dimension unchanged. $out_orig_crop_dim_shorter = $in_orig_shorter; //Set the cropping of the longer side of the original to cover half the necessary //reduction. $out_orig_crop_start_longer = round(((float)$in_orig_longer - (float)$out_orig_crop_dim_longer) / 2.0); settype($out_orig_crop_start_longer, "integer"); //No cropping of original shorter side. $out_orig_crop_start_shorter = 0; } //-------------------------------------------------------------------------------- //Repeats a character to the console output a certain number of times. function rep_char_con($c, $n) { while ($n--) echo $c; } //-------------------------------------------------------------------------------- //Writes a standard thick horizontal line to the console. function hor_line_thick() { rep_char_con("=", CFG_CONSOLE_STD_LINE_LEN); echo "\n"; } //-------------------------------------------------------------------------------- //Writes a standard thin horizontal line to the console. function hor_line_thin() { rep_char_con("-", CFG_CONSOLE_STD_LINE_LEN); echo "\n"; } //-------------------------------------------------------------------------------- //Returns the name of the file that should be used for the index. function index_file_name() { if (CFG_DTA_PHP_INDEX_CREATE) return CFG_PHP_INDEX_FILE_NAME; else return CFG_HTML_INDEX_FILE_NAME; } //-------------------------------------------------------------------------------- //Checks if the index file already exists, returns TRUE if so, or FALSE otherwise. //Because the sole purpose of this function is to refuse to write over an //existing index file, it will also return true if a directory of the target //name exists. function index_file_or_dir_exists() { return file_exists(index_file_name()); } //-------------------------------------------------------------------------------- //Returns an array of all files in the working directory. //If no files can be found, returns FALSE. function get_file_names_in_dir() { //Get directory list. $rv = scandir ("."); //If the list is empty, something went wrong. Return FALSE. if ($rv === FALSE) return FALSE; return $rv; } //-------------------------------------------------------------------------------- //Returns TRUE if a file name appears to be a valid .JPG file name, or FALSE //otherwise. function is_valid_jpg_file_name($s) { //First, the file name has to be AT LEAST 5 characters long. This would //be a short name like "x.jpg". if (strlen($s) < 5) return FALSE; //Convert the string name to all lower case. $s = strtolower($s); //If the final 4 characters are .jpg, we call it good. We've already //verified that the string is at least of length 5. $s2 = substr($s, strlen($s) - 4); if (strcmp($s2, ".jpg") == 0) return TRUE; //The only remaining possibility for validity is that the string is of the //form ".jpeg". Check for this. //The file name has to be AT LEAST 6 characters long. This would //be a short name like "x.jpeg". if (strlen($s) < 6) return FALSE; //If the final 5 characters are .jpeg, we call it good. We've already //verified that the string is at least of length 6. $s2 = substr($s, strlen($s) - 5); if (strcmp($s2, ".jpeg") == 0) return TRUE; //If we are here, not a valid .jpg file name. return FALSE; } //-------------------------------------------------------------------------------- //As a function of the file name, creates the file name for the thumbnail. function file_name_to_thumbnail_name($in_filename) { $extension_start = strrpos($in_filename, "."); //Find position of last "." in string. This should precede the //file extension. if ($extension_start === FALSE) { //"." not found. Should not happen. Filenames were checked in advance. echo "Fatal internal error at line " . __LINE__ . "\n"; exit(1); } $filename_prefix = substr($in_filename, 0, $extension_start); $filename_extension = substr($in_filename, $extension_start); $rv = $filename_prefix . CFG_THUMBNAIL_FILENAME_SUFFIX . $filename_extension; return $rv; } //-------------------------------------------------------------------------------- //Writes the index file preamble to the index file. This is very much //dependent on how the thumbnails will be used. This is currently customized //for what Dave Ashley does. function write_index_file_preamble($handle) { fwrite($handle, "<" . "?" . "php\n"); fwrite($handle, "require_once(\"style/std/stdwpstyle.inc\");\n"); fwrite($handle, "?" . ">" . "\n"); fwrite($handle, "<" . "?" . "php\n"); fwrite($handle, " $" . "thispage = new StdWpStyle();\n"); fwrite($handle, "\n"); fwrite($handle, " $" . "thispage->static_page_header_title_std(\"TBD\",\n"); fwrite($handle, " \"TBD\",\n"); fwrite($handle, " \"\");\n"); fwrite($handle, " $" . "thispage->static_page_photo_thumbnail_explanation(TRUE, TRUE);\n"); fwrite($handle, "?" . ">" . "\n"); for ($i = 0; $i < CFG_INDEX_PARAGRAPH_INDENT; $i++) { fwrite($handle, " "); } fwrite($handle, "\n

\n"); } //-------------------------------------------------------------------------------- //Writes the index file postamble to the index file. This is very much //dependent on how the thumbnails will be used. This is currently customized //for what Dave Ashley does. function write_index_file_postamble($handle) { for ($i = 0; $i < CFG_INDEX_PARAGRAPH_INDENT; $i++) { fwrite($handle, " "); } fwrite($handle, "

\n\n"); fwrite($handle, "<" . "?" . "php\n"); fwrite($handle, " $" . "thispage->static_page_footer_std();\n"); fwrite($handle, "?" . ">" . "\n"); } //-------------------------------------------------------------------------------- //Actually creates the thumbnail, and returns some information about what was //done. // //The calls to the ImagMagick library take on the order of 5s per image if there //is filtering. function create_thumbnail( $in_filename, $in_thumbnailname, & $out_filename_filesize, & $out_filename_xdim, & $out_filename_ydim, & $out_thumbnailname_filesize, & $out_thumbnailname_xdim, & $out_thumbnailname_ydim) { //Assign output parameters just in case something doesn't get assigned. $out_filename_filesize = 0; $out_filename_xdim = 0; $out_filename_ydim = 0; $out_thumbnailname_filesize = 0; $out_thumbnailname_xdim = 0; $out_thumbnailname_ydim = 0; //Establish target dimensions. Two cases, depending on which is the longer //side. //Construct. $imagick = new Imagick(); //Load image. $imagick->readImage($in_filename); //Get the dimensions of the image we just loaded. $geo = $imagick->getImageGeometry(); $out_filename_xdim = $geo['width']; $out_filename_ydim = $geo['height']; //Calculate target sizes. We rearrange parameters based on which is our //longest side. if ($out_filename_xdim >= $out_filename_ydim) { //Longer width (x-dimension), or square. calc_thumbnail_conversion_pars ( $out_filename_xdim, $out_filename_ydim, CFG_THUMBNAIL_DIMENSION_MAX, $out_thumbnailname_xdim, $out_thumbnailname_ydim, $orig_crop_dim_x, $orig_crop_dim_y, $orig_crop_start_x, $orig_crop_start_y ); } else { //Longer height (y-dimension). calc_thumbnail_conversion_pars ( $out_filename_ydim, $out_filename_xdim, CFG_THUMBNAIL_DIMENSION_MAX, $out_thumbnailname_ydim, $out_thumbnailname_xdim, $orig_crop_dim_y, $orig_crop_dim_x, $orig_crop_start_y, $orig_crop_start_x ); } //For debugging only, might want to know intermediate calculation results. //echo "xcropdim, ycropdim, xcropstart, ycropstart: " // . // $orig_crop_dim_x // . // " " // . // $orig_crop_dim_y // . // " " // . // $orig_crop_start_x // . // " " // . // $orig_crop_start_y // . // "\n"; //Crop the original to try to preserve the aspect ratio of the thumbnail //as precisely as possible. $imagick->cropImage( $orig_crop_dim_x, $orig_crop_dim_y, $orig_crop_start_x, $orig_crop_start_y ); //For debugging only, might want to get a look at the cropped image, to //be sure nothing unexpected happens on the canvas. //$imagick->writeImage($in_filename . ".cropped.jpg"); //Resize to thumbnail size. if (CFG_LANCZOS_FILTER_APPLY) { $imagick->resizeImage($out_thumbnailname_xdim, $out_thumbnailname_ydim, Imagick::FILTER_LANCZOS, 1); } else { $imagick->resizeImage($out_thumbnailname_xdim, $out_thumbnailname_ydim, 0, 1); } //Create the border. $imagick->raiseImage(CFG_THUMBNAIL_BEVELED_BORDER_WIDTH, CFG_THUMBNAIL_BEVELED_BORDER_WIDTH, 0, 0, 1); //Set compression to get a smaller thumbnail written, and strip //header information. stripImage() seems to have the largest effect //on thumbnail file size, so leaving the thumbnail quality near 100% //is feasible. The jump in file size between 90% and 95% seemed to be //fairly large (40% to 50%), so I left it at 90%. My rationale is //that with the proliferation of mobile devices and cellular data, //getting the thumbnail as small as possible is more important than //the thumbnail looking perfect. If the viewer wants a perfect image, //they can view the full-sized image. $imagick->setImageCompression(Imagick::COMPRESSION_JPEG); $imagick->setImageCompressionQuality(90); $imagick->stripImage(); //Write the thumbnail. $imagick->writeImage($in_thumbnailname); //Destroy to prevent memory leak. $imagick->clear(); $imagick->destroy(); //All of the writing is done. Try to obtain the file sizes. $fsize = filesize($in_filename); if ($fsize !== FALSE) $out_filename_filesize = $fsize; $fsize = filesize($in_thumbnailname); if ($fsize !== FALSE) $out_thumbnailname_filesize = $fsize; } //-------------------------------------------------------------------------------- //Writes a line to the index file corresponding to a linked larger picture //reprsented by a thumbnail. function write_index_line( $handle, $filename, $thumbnailname, $filename_filesize, $filename_xdim, $filename_ydim, $thumbnailname_filesize, $thumbnailname_xdim, $thumbnailname_ydim) { //
dscn1258.jpg (4932338 bytes) //Indent. for ($i = 0; $i < CFG_INDEX_LINK_INDENT; $i++) { fwrite($handle, " "); } fwrite($handle, "\"");"); fwrite($handle, "\n"); } //-------------------------------------------------------------------------------- //Write introductory message. hor_line_thick(); echo "Execution begins.\n"; hor_line_thin(); //----------------------------------------------------------------------------- //End the script of the index file already exists. Don't want to overwrite //anything that may be valuable to the user. if (index_file_or_dir_exists()) { echo "Index file \"" . index_file_name() . "\" already exists.\n"; echo "Script cannot continue. Exiting.\n"; exit(1); } else { echo "Index file \"" . index_file_name() . "\" does not exist. " . "Continuing.\n"; } hor_line_thin(); //----------------------------------------------------------------------------- //Get and emit the names of everything in the directory. $file_list = get_file_names_in_dir(); if ($file_list === FALSE) { echo "List of files from PHP function scandir() is empty.\n"; echo "Serious internal error, or nothing to do. Script cannot continue.\n"; exit(1); } else { echo "Files in working directory (unsorted, unfiltered, " . count($file_list) . " files):\n"; for ($i = 0; $i < count($file_list); $i++) echo " " . sprintf("[%5d]", $i) . " " . $file_list[$i] . "\n"; } hor_line_thin(); //----------------------------------------------------------------------------- //Figure out which file names are valid for .JPG files and create a //list containing only those. $found = FALSE; for ($i = 0; $i < count($file_list); $i++) { if (is_valid_jpg_file_name($file_list[$i])) { $found = TRUE; $staging_list[] = $file_list[$i]; } } if (!$found) { echo "There were no filenames corresponding to JPEG files.\n"; echo "Script cannot continue.\n"; exit(1); } $file_list = $staging_list; unset($staging_list); //----------------------------------------------------------------------------- //Sort the filenames, and provide the user with a list. sort($file_list, SORT_STRING); echo "JPEG file to be indexed (filtered, sorted, " . count($file_list) . " files):\n"; for ($i = 0; $i < count($file_list); $i++) echo " " . sprintf("[%5d]", $i) . " " . $file_list[$i] . "\n"; hor_line_thin(); //----------------------------------------------------------------------------- //Create a parallel list that contains the proposed names of files with new //names that are all lower-case. If the lower-casing option is turned off, //just create a parallel identical list. for ($i = 0; $i < count($file_list); $i++) { if (CFG_IMAGE_LC_CONVERT) $file_list_lc[$i] = strtolower($file_list[$i]); else $file_list_lc[$i] = $file_list[$i]; } //----------------------------------------------------------------------------- //Look for collisions between the two lists, and abort if there are collisions. //A collision essentially means that at least one file is differentiated from //at least one other file based only on case. This kind of lazy comparison //is quadratic, but with 10,000 files it would involve about 10,000,000 //comparisons--not too bad. But for any serious computing, this couldn't be //done that way it is. $collisions = 0; for ($i = 0; $i < count($file_list); $i++) { for ($j = 0; $j < count($file_list); $j++) { if (($i != $j) && (strcmp($file_list[$i], $file_list_lc[$j]) == 0)) $collisions++; } } if ($collisions != 0) { echo "There are filename collisions that have resulted from filenames\n"; echo "differing only in case. The script cannot continue.\n"; exit(1); } //----------------------------------------------------------------------------- //Convert any filenames that need it to lower case. if (CFG_IMAGE_LC_CONVERT) { for ($i = 0; $i < count($file_list); $i++) { if (strcmp($file_list[$i], $file_list_lc[$i]) == 0) { echo "Skipped rename of \"" . $file_list[$i] . "\" to lower case.\n"; } else { $result = rename($file_list[$i], $file_list_lc[$i]); if ($result === TRUE) { echo "Renamed \"" . $file_list[$i] . "\" to \"" . $file_list_lc[$i] . "\".\n"; } else { echo "Rename of \"" . $file_list[$i] . "\" to \"" . $file_list_lc[$i] . "\" failed.\n"; echo "The script cannot continue.\n"; exit(1); } } } hor_line_thin(); } //----------------------------------------------------------------------------- //At this point, the lower-case list can be discarded. //----------------------------------------------------------------------------- $file_list = $file_list_lc; unset($file_list_lc); //----------------------------------------------------------------------------- //Create a parallel list of thumbnail file names. for ($i = 0; $i < count($file_list); $i++) { $thumbnail_list[$i] = file_name_to_thumbnail_name($file_list[$i]); //echo $thumbnail_list[$i] . "\n"; } //----------------------------------------------------------------------------- //Check for duplicates. A duplicate might occur, for example, if "x.jpg" //and "x_small.jpg" both exist. $collisions = 0; for ($i = 0; $i < count($file_list); $i++) { for ($j = 0; $j < count($thumbnail_list); $j++) { if (($i != $j) && (strcmp($file_list[$i], $file_list_lc[$j]) == 0)) $collisions++; } } if ($collisions != 0) { echo "There are filename collisions that have resulted when trying\n"; echo "to find thumbnail names. Very likely some thumbnails already\n"; echo "exist. The script cannot continue.\n"; exit(1); } //----------------------------------------------------------------------------- //Open up the index file for writing. Abort script if unsuccessful. $index_handle = fopen(index_file_name(), "w"); if ($index_handle === FALSE) { echo "Failed to open \"" . index_file_name() . "\" for writing.\nScript cannot continue.\n"; exit(1); } else { echo "Successfully opened \"" . index_file_name() . "\" for writing.\n"; hor_line_thin(); } //----------------------------------------------------------------------------- //Write the index file preamble to the index file. The preamble is everything //up until the lines that reference the thumbnails. write_index_file_preamble($index_handle); //----------------------------------------------------------------------------- //For each file, create the thumbnail and write the line in the index file. for ($i = 0; $i < count($file_list); $i++) { echo "Creating thumbnail \"" . $thumbnail_list[$i] . "\" from image \"" . $file_list[$i] . "\".\n"; create_thumbnail($file_list[$i], $thumbnail_list[$i], $filename_filesize, $filename_xdim, $filename_ydim, $thumbnail_filesize, $thumbnail_xdim, $thumbnail_ydim); echo "Conversion complete.\n"; echo " Full-sized image file size/xdim/ydim = " . $filename_filesize . "/" . $filename_xdim . "/" . $filename_ydim . ",\n"; echo " Thumbnail image filesize/xdim/ydim = " . $thumbnail_filesize . "/" . $thumbnail_xdim . "/" . $thumbnail_ydim . ",\n"; write_index_line($index_handle, $file_list[$i], $thumbnail_list[$i], $filename_filesize, $filename_xdim, $filename_ydim, $thumbnail_filesize, $thumbnail_xdim, $thumbnail_ydim); hor_line_thin(); } //----------------------------------------------------------------------------- //Write the index file postamble to the index file. The postamble is //everything after the lines that reference the thumbnails. write_index_file_postamble($index_handle); //----------------------------------------------------------------------------- //Close the index file, not checking for error codes. fclose($index_handle); //----------------------------------------------------------------------------- echo "Execution ends.\n"; hor_line_thick(); //-------------------------------------------------------------------------------- //End of File //-------------------------------------------------------------------------------- ?>