= strlen(CFG_THUMBNAIL_FILENAME_SUFFIX)) { if (substr($filename_base, strlen($filename_base) - strlen(CFG_THUMBNAIL_FILENAME_SUFFIX)) == CFG_THUMBNAIL_FILENAME_SUFFIX) return FALSE; } //Looks good. return TRUE; } //-------------------------------------------------------------------------------- //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; } //-------------------------------------------------------------------------------- //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; } //-------------------------------------------------------------------------------- //Write introductory message. hor_line_thick(); echo CFG_PROGNAME . " Copyright (C) 2015 David T. Ashley\n"; echo "This program comes with ABSOLUTELY NO WARRANTY; and is licensed under\n"; echo "the GNU General Public License, Version 3. A copy of this license is\n"; echo "provided in the source code of this program.\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"; hor_line_thick(); 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(); //----------------------------------------------------------------------------- //Remove the standard directory entries "." and ".." from the list, and //remove any directories. $temp_list = $file_list; unset($file_list); $n = 0; for ($i = 0; $i < count($temp_list); $i++) { //echo "Checking " . $temp_list[$i] . "\n"; if (strcmp($temp_list[$i], ".") == 0) { //. entry, not a file. } else if (strcmp($temp_list[$i], "..") == 0) { //.. entry, not a file. } else if (is_file($temp_list[$i])) { //This is a regular file. $file_list[] = $temp_list[$i]; $n++; } } if ($n == 0) $file_list = FALSE; unset($n); unset($temp_list); //----------------------------------------------------------------------------- //If there is nothing to do, end the script. if ($file_list === FALSE) { echo "No files to process.\n"; hor_line_thick(); exit(0); } //----------------------------------------------------------------------------- //Sort the list. This is a non-event. The only rationale for sorting is that //it ensures that the same set of files will be processed in the same order, //regardless of the order provided by the underlying OS internals. sort($file_list); //----------------------------------------------------------------------------- //Emit the names we now have. echo "Files in working directory (directory entries removed, 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(); //----------------------------------------------------------------------------- //For each file that is appropriate and where the thumbnail does not already //exist, up to the maximum we may do in one invocation, create the thumbnail. $i = 0; $completed = 0; while (($completed < CFG_MAX_THUMBNAILS_PER_INVOCATION) && ($i < count($file_list))) { if (is_full_sized_image_file_name($file_list[$i])) { $thumbnail_name = file_name_to_thumbnail_name($file_list[$i]); if (file_exists($thumbnail_name)) { echo " " . sprintf("[%5d]", $i) . " " . $file_list[$i] . " : skipping because corresponding thumbnail exists.\n"; hor_line_thin(); } else { echo " " . sprintf("[%5d]", $i) . " " . $file_list[$i] . " : creating thumbnail.\n"; echo "Creating thumbnail \"" . $thumbnail_name . "\" from image \"" . $file_list[$i] . "\".\n"; create_thumbnail($file_list[$i], $thumbnail_name, $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"; hor_line_thin(); $completed++; } } else { //Unsuitable base name. Can't use it. echo " " . sprintf("[%5d]", $i) . " " . $file_list[$i] . " : skipping due to unsuitable name.\n"; hor_line_thin(); } $i++; } //Emit a message about whether the program should be run again. I am aware of //the uncovered case--where the last thumbnail was made on the last iteration //of this invocation--but I will leave it uncovered for now. All that happens //is the user runs the program unnecessarily one more time. if ($completed == 0) { echo "No thumbnails were created--this program is done creating thumbnails.\n"; echo "It is not necessary to run this program again.\n"; hor_line_thin(); } else { echo $completed . " thumbnail(s) were created. Please run this program repeatedly again\n"; echo "until no more thumbnails are created.\n"; hor_line_thin(); } echo CFG_PROGNAME . " execution ends.\n"; hor_line_thick(); //-------------------------------------------------------------------------------- //End of File //-------------------------------------------------------------------------------- ?>