Computers

Using PHP to sort Wallpapers by Dimension

Once upon a time, I found myself in possession of a vast amount of wallpapers. I’m talking well over 10,000, all in one folder. That’s not to helpful since a lot of them were repeats in different dimensions. After realizing that I couldn’t just add a ‘sort by dimension’ field in explorer, I set about on a mission using a method I knew would work, PHP with the GD (Graphics Display) library.

The core of this script is based around the ability for GD to pull the dimensions of a picture quickly and efficiently. The goal is to get php to look in a directory and sort all their files by dimensions we define (this will help filter out any pictures in other dimensions present in the folder).

So the sorting cycle looks something like this:

$totalFiles=count($file);
for ($j=0; $j < $totalFiles; $j++)
{
    $dimension=getimagesize("$dir$file[$j]");
    for($k=0; $k < $totalFolders; $k++)
        if($dimension[0] == $width[$k] && $dimension[1] == $height[$k])
        {
            movefile($dir, $file[$j], "$dir$width[$k]x$height[$k]");
            $sorted++;
            if ($sorted%50==0) { echo $sorted." files sorted | ".round(100*($sorted/$totalFiles),2)."%\n";        }
    }
}

However, the folder is currently non-existent but we're not going to open up explorer and create them ourself, especially since we plan on having an array hold our dimension values.

Here's what I did:

$width = array(800,1024,1152,1280,1280,1280,1440,1600,1680,1920);
$height = array(600,768,864,800,960,1024,900,1200,1050,1200);

if (count($width) != count($height)) // Make sure the arrays are the same length
    exit ("Non-matching array lengths: ".count($width)." widths with ".count($height)." heights.");

$totalFolders=count($width);
for($i=0; $i < $totalFolders; $i++)  // Create directories based on the arrays if they haven't been created already
    if (!file_exists("$dir$width[$i]x$height[$i]"))
    {
        mkdir("$dir$width[$i]x$height[$i]");
        echo "Folder $width[$i]x$height[$i] created\n";
    }

The rest of the code is all pretty straight forward. I added in some job statistics (percentages and amount of time to complete). I have to find my pictures of the output but I believe it went through all the wallpapers in ~110 seconds. Anyway, here's the script in it's entirety, I hope it helps :).

<?php
$dir = "pics/";                      // The directory (trailing slash)
$dh = opendir($dir);                 // Open the directory
$m_time = explode(" ",microtime());  // These two lines get the start time
$starttime = $m_time[0] + $m_time[1];//
$width = array(800,1024,1152,1280,1280,1280,1440,1600,1680,1920);
$height = array(600,768,864,800,960,1024,900,1200,1050,1200);

if (count($width) != count($height)) // Make sure the arrays are the same length
    exit ("Non-matching array lengths: ".count($width)." widths with ".count($height)." heights.");

$totalFolders=count($width);
for($i=0; $i < $totalFolders; $i++)  // Create directories based on the arrays if they haven't been created already
    if (!file_exists("$dir$width[$i]x$height[$i]"))
    {
        mkdir("$dir$width[$i]x$height[$i]");
        echo "Folder $width[$i]x$height[$i] created\n";
    }
// Create an array of the file names
while (false !== ($filename = readdir($dh))) {
   if (!is_dir($dir.$filename)) $file[] = $filename;
}
if (!isset($file))
    exit ("No files in this directory.");

$sorted=0;
$totalFiles=count($file);
for ($j=0; $j < $totalFiles; $j++)
{
    $dimension=getimagesize("$dir$file[$j]");
    for($k=0; $k < $totalFolders; $k++)
        if($dimension[0] == $width[$k] && $dimension[1] == $height[$k])
        {
            movefile($dir, $file[$j], "$dir$width[$k]x$height[$k]");
            $sorted++;
            if ($sorted%50==0) { echo $sorted." files sorted | ".round(100*($sorted/$totalFiles),2)."%\n";        }
    }
}

$m_time = explode(" ",microtime());  // These two lines get the end time
$endtime = $m_time[0] + $m_time[1];  //
echo "\n".$sorted." files have been sorted in ". round(($endtime - $starttime),3) ." seconds.\n";
echo "That's ".(1/(round(($endtime - $starttime),3)/$sorted))." files per second!";

function movefile($dir, $theFile, $newDir)
{
    if (!copy($dir.$theFile, "$newDir/$theFile")) echo "failed to copy $theFile...\n";
    else unlink($dir.$theFile);
}
?>

Note: This script should be called from the command line.

PHP Excel File Count - Screenshot 1

Using PHP to Make Excel Easier

Recently, I was presented with the task of counting the number times filenames repeated in an excel document. Being a 16,000+ row file, with well over 500 unique filenames, there was no simple solution in excel.

Having some experience exporting excel documents using PHP, I knew importing them wouldn’t be too hard. A few google searches later and I came across Excel Reader which fit the bill perfectly.

After sorting my excel document by filename, I created a loop in PHP that would read the first filename, count the number of times it repeated until it changed, then append that data to a string that would be exported to a new excel document once it was done reading the data.

// Starting at 2 to skip the headers | +1 so it compares the last row
for ($i = 2; $i <= $data->sheets[0]['numRows']+1; $i++) {
	if ($data->sheets[0]['cells'][$i][3]==$curFileName) {
		$curFileCount++;
	} else {
		// Output the data line:
"Device","User","Filename","Filecount"
		$stringData =
"\"".$data->sheets[0]['cells'][$i-1][1]."\",\"".$data->sheets[0]['cells'][$i
-1][2]."\",\"".$data->sheets[0]['cells'][$i-1][3]."\",\"".$curFileCount."\"\
n";
		fwrite($fh, $stringData);

		// Reset the count and update the curFileName
		$curFileName = $data->sheets[0]['cells'][$i][3];
		$curFileCount=1;
	}
	echo "Row: ".$i."\n";
}

Of course… I didn’t have just one of these files but 6 and manually entering each filename would be a waste of my time. My software solution for this was to create an “Input” and “Output” folder and have PHP cycle through the “Input” folder.

// For every file in the dir "Input", generate a report
if ($handle = opendir('Input')) {
	while (false !== ($orgFileName = readdir($handle))) {
		if($orgFileName != '..' && $orgFileName != '.') {

			// Echo the file we're reading, then read that file
			echo $orgFileName."\n";
			$data->read("Input/".$orgFileName);

Eventually it all came together as such:

<?php

// Don't change any of this
require_once 'Excel/reader.php';
$data = new Spreadsheet_Excel_Reader();
$data->setOutputEncoding('CP1251');

// For every file in the dir "Input", generate a report
if ($handle = opendir('Input')) {
	while (false !== ($orgFileName = readdir($handle))) {
		if($orgFileName != '..' && $orgFileName != '.') {

			// Echo the file we're reading
			echo $orgFileName."\n";
			$data->read("Input/".$orgFileName);

			// Set the first filename to compare
			$curFileName =
$data->sheets[0]['cells'][2][3]==$curFileName;
			$curFileCount = 0;

			// Create a file and open it for writing
			$myFile =
"Output/File_Count-".str_replace(".xls",".csv",$orgFileName);
			$fh = fopen($myFile, 'w') or die("can't open file");

			// Starting at 2 to skip the headers | +1 so it
compares the last row
			for ($i = 2; $i <= $data->sheets[0]['numRows']+1;
$i++) {
				if
($data->sheets[0]['cells'][$i][3]==$curFileName) {
					$curFileCount++;
				} else {
					// Output the data line:
"Device","User","Filename","Filecount"
					$stringData =
"\"".$data->sheets[0]['cells'][$i-1][1]."\",\"".$data->sheets[0]['cells'][$i
-1][2]."\",\"".$data->sheets[0]['cells'][$i-1][3]."\",\"".$curFileCount."\"\
n";
					fwrite($fh, $stringData);

					// Reset the count and update the
curFileName
					$curFileName =
$data->sheets[0]['cells'][$i][3];
					$curFileCount=1;
				}
				echo "Row: ".$i."\n";
			}
			// Close file
			fclose($fh);
		}
	}
}
?>

My apologies for the less than perfect code, I was on a deadline and perfect code wasn’t a requirement :).

Here are some screenshots of the script in action.

PHP Excel File Count - Screenshot 1

PHP Excel File Count - Screenshot 2

PHP Excel File Count - Screenshot 3

Litebox 1.0 Released

I know… this seems silly because I released 1.0a today… but I honestly didn’t think I’d get this done today.

1.0a links are down for a short while. I want to clean up the script a bit.

litebox 1.0 get it while it’s hot!