tags. * If disabled, standard tags will be used instead. * * Data URIs aren't supported by older browsers, but using them significantly reduces the number of HTTP requests required to load a page */ @define('USE_DATA_URIS', true); /** * Rotate images, as required by their orientation flag, using CSS transforms * * Only one of CSS_ROTATION or PHP_ROTATION should be used */ @define('CSS_ROTATION', false); /** * Rotate images, as required by their orientation flag, using PHP's gd function * * Only one of CSS_ROTATION or PHP_ROTATION should be used */ @define('PHP_ROTATION', true); /** * How long cached geocode data will be considered valid, after which it will be reloaded from Google * * Defaults to 2678400, or 31 days */ @define('GEOCODE_CACHE_TIMEOUT', 2678400); /** * Whether to gzip compress the geocode data cache files. * * These cache files are surprisingly large (around 15 kB each) and are highly compressible (generally reduced to less than 10% of their original size) so leaving this enabled is recommended unless CPU resources are costly while there is a surplus of disk space */ @define('GEOCODE_GZIP_CACHE', true); class Formatting { public static function formatFilesize($bytes, $decimalPlaces=2, $includeTrailingZeroes=false, $useBinaryPrefixesForFilesizes=true) { if ($useBinaryPrefixesForFilesizes) { $suffixes = array( 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB' ); } else { $suffixes = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ); } $suffix = 0; while($bytes >= 1024) { $bytes /= 1024; $suffix++; } $r = number_format(round($bytes, $decimalPlaces), $decimalPlaces, '.', ','); if (!$includeTrailingZeroes) { if (strpos($r, '.') !== false) { //Trim off trailing zeroes, then the trailing decimal point, if necessary (check // for the existence of a decimal point first, or we can end up stripping trailing // zeroes BEFORE the decimal point, thus completely altering the displayed value!) $r = rtrim(rtrim($r, '0'), '.'); } } return $r . ' ' . $suffixes[$suffix]; } } class NoExifDataException extends Exception {} class Coordinates { private $latRef; private $latDeg; private $latMin; private $latSec; private $lonRef; private $lonDeg; private $lonMin; private $lonSec; private $latDecimal; private $lonDecimal; public function __construct($latRef, $latDeg, $latMin, $latSec, $lonRef, $lonDeg, $lonMin, $lonSec) { $this->latRef = $latRef; $this->latDeg = $latDeg; $this->latMin = $latMin; $this->latSec = $latSec; $this->lonRef = $lonRef; $this->lonDeg = $lonDeg; $this->lonMin = $lonMin; $this->lonSec = $lonSec; $this->calculateDecimalPair(); } private static function parseFraction($string) { return eval("return $string;"); } public static function fromExifArrays($latRef, array $lat, $lonRef, array $lon) { return new Coordinates( $latRef, self::parseFraction($lat[0]), self::parseFraction($lat[1]), self::parseFraction($lat[2]), $lonRef, self::parseFraction($lon[0]), self::parseFraction($lon[1]), self::parseFraction($lon[2]) ); } /* ["GPSVersion"]=> string(3) "" ["GPSLatitudeRef"]=> string(1) "N" ["GPSLatitude"]=> array(3) { [0]=> string(4) "51/1" [1]=> string(4) "17/1" [2]=> string(7) "164/100" ["GPSLongitudeRef"]=> string(1) "W" ["GPSLongitude"]=> array(3) { [0]=> string(3) "1/1" [1]=> string(3) "4/1" [2]=> string(8) "5982/100" ["GPSAltitudeRef"]=> string(1) "" ["GPSAltitude"]=> string(3) "0/1" ["GPSTimeStamp"]=> array(3) { [0]=> string(4) "14/1" [1]=> string(4) "56/1" [2]=> string(4) "12/1" ["GPSMapDatum"]=> string(6) "WGS-84" ["GPSProcessingMode"]=> string(15) "ASCIINETWORK" ["GPSDateStamp"]=> string(10) "2011:07:19" */ public function toPrettyCoordinates() { // N51°17'1.64" W1°4'59.82" return "{$this->latRef}{$this->latDeg}°{$this->latMin}'{$this->latSec}\" {$this->lonRef}{$this->lonDeg}°{$this->lonMin}'{$this->lonSec}\""; } public function __toString() { return "{$this->queryLocationName()} ({$this->toPrettyCoordinates()} / {$this->toDecimalString()})"; } private function calculateDecimalPair() { $lat = $lon = 0; $lat += $this->latDeg + ($this->latMin / 60) + ($this->latSec / 60 / 60); if ($this->latRef == 'S') $lat *= -1; $lon += $this->lonDeg + ($this->lonMin / 60) + ($this->lonSec / 60 / 60); if ($this->lonRef == 'W') $lon *= -1; $this->latDecimal = number_format(round($lat, 5), 5); $this->lonDecimal = number_format(round($lon, 5), 5); } public function toDecimalPair() { return array($this->latDecimal, $this->lonDecimal); } public function toDecimalString() { list($lat, $lon) = $this->toDecimalPair(); return "$lat,$lon"; } private $locationNameCache; public function queryLocationName() { if (!isset($this->locationNameCache)) { $cacheDecimalValues = round($this->latDecimal, 4) . ',' . round($this->lonDecimal, 4); if (GEOCODE_GZIP_CACHE) { $cacheFilename = sys_get_temp_dir() . "/geocode_$cacheDecimalValues.cache.gz"; $cacheFilenameWrapper = "compress.zlib://$cacheFilename"; } else { $cacheFilenameWrapper = $cacheFilename = sys_get_temp_dir() . "/geocode_$cacheDecimalValues.cache"; } if (!file_exists($cacheFilename) || filemtime($cacheFilename) < (time() - GEOCODE_CACHE_TIMEOUT)) { $jsonString = file_get_contents("http://maps.googleapis.com/maps/api/geocode/json?latlng={$this->toDecimalString()}&sensor=false"); file_put_contents($cacheFilenameWrapper, $jsonString); } else { $jsonString = file_get_contents($cacheFilenameWrapper); } $json = json_decode($jsonString); if ($json->status != 'OK') { $this->locationNameCache = "[LOCATION REQUEST FAILED]"; unlink($cacheFilename); } else { $this->locationNameCache = $json->results[0]->formatted_address; } } return $this->locationNameCache; } } class ExifData { const ORIENTATION_STANDARD = 1; const ORIENTATION_FLIP_HORIZONTAL = 2; const ORIENTATION_ROTATE_180 = 3; const ORIENTATION_FLIP_VERTICAL = 4; const ORIENTATION_FLIP_VERTICAL_ROTATE_270 = 5; const ORIENTATION_ROTATE_270 = 6; const ORIENTATION_FLIP_HORIZONTAL_ROTATE_270 = 7; const ORIENTATION_ROTATE_90 = 8; private $model; private $make; private $orientation; private $datetime; private $exposure; private $fNumber; private $isoSpeed; private $shutterSpeed; private $aperture; private $flash; private $focalLength; private $coordinates; public function __construct($filename) { $data = @exif_read_data($filename, 'EXIF'); if (!$data) throw new NoExifDataException("Unable to find EXIF data for '$filename'"); $this->model = @$data['Model']; $this->make = @$data['Make']; $this->orientation = @$data['Orientation']; $this->datetime = !empty($data['DateTimeOriginal']) ? strtotime($data['DateTimeOriginal']) : strtotime($data['DateTime']); $this->exposure = @$data['ExposureTime']; $this->fNumber = @$data['FNumber']; $this->isoSpeed = @$data['ISOSpeedRatings']; $this->shutterSpeed = @$data['ShutterSpeedValue']; $this->aperture = @$data['ApertureValue']; $this->flash = @$data['Flash']; $this->focalLength = @$data['FocalLength']; if (isset($data['GPSLatitudeRef'], $data['GPSLatitude'], $data['GPSLongitudeRef'], $data['GPSLongitude'])) { $this->coordinates = Coordinates::fromExifArrays($data['GPSLatitudeRef'], $data['GPSLatitude'], $data['GPSLongitudeRef'], $data['GPSLongitude']); } } private static function _getFloat($value) { $pos = strpos($value, '/'); if ($pos === false) return (float) $value; $a = (float) substr($value, 0, $pos); $b = (float) substr($value, $pos+1); $out = ($b == 0) ? ($a) : ($a / $b); return $out; } private static function getFloat($value) { return number_format(round(self::_getFloat($value), 2), 2); } public function getModel() { return $this->model; } public function getMake() { return $this->make; } public function getOrientation() { return $this->orientation; } public function getDateTime() { return $this->datetime; } public function getExposure() { return self::getFloat($this->exposure); } public function getFNumber() { $apex = self::getFloat($this->aperture); $fstop = pow(2, $apex/2); if ($fstop == 0) return false; return 'f/' . round($fstop,1); } public function getIsoSpeed() { return $this->isoSpeed; } public function getShutterSpeed() { $apex = self::getFloat($this->shutterSpeed); $shutter = pow(2, -$apex); if ($shutter == 0) return false; if ($shutter >= 1) return round($shutter) . ' secs'; return '1/' . round(1 / $shutter) . ' secs'; } public function getAperture() { return self::getFloat($this->aperture); } public function getFlash() { return $this->flash; } public function getFocalLength() { return self::getFloat($this->focalLength); } public function getCoordinates() { if (!$this->coordinates) { throw new Exception("Coordinates not set"); } return $this->coordinates; } public function getCoordinateString() { if ($this->coordinates) { return "{$this->coordinates}"; } else { return ''; } } } interface IMediaInfo { public function __construct($filename); public function getFilename(); public function getBasename($stripExtension=false); public function getFilesize(); public function getFilesizeHumanReadable($decimalPlaces=2, $includeTrailingZeroes=true); public function getWidth(); public function getHeight(); public function getType(); public function getMimeType(); public function getResizedDimensions($maxWidth, $maxHeight); public function getResizedImgTagSizesString($maxWidth, $maxHeight); public function getThumbnail($maxWidth, $maxHeight, $return=false); public function getOrientation(); public function getRotationDegrees($clockwise=false); public function getExifData(); public function getExtraMetaData(); } class ImageInfo implements IMediaInfo { private $filename; private $width; private $height; private $type; private $mimeType; public function __construct($filename, array $info=array()) { // 0 = width; 1 = height; 2 = IMAGETYPE_* const; 3 = tag size string; mime = mime type string; channels = channel count (3 for RGB, 4 for CMYK), bits = colour bit count $this->filename = $filename; if (!$info) { $info = getimagesize($filename); if (!$info) throw new Exception("unable to fetch image data for '$filename'"); } $this->width = $info[0]; $this->height = $info[1]; $this->type = $info[2]; $this->mimeType = $info['mime']; } public function getFilename() { return $this->filename; } public function getBasename($stripExtension=false) { $info = pathinfo($this->filename); if ($stripExtension) { return $info['filename']; } else { return $info['basename']; } } public function getFilesize() { return filesize($this->filename); } public function getFilesizeHumanReadable($decimalPlaces=2, $includeTrailingZeroes=true) { return Formatting::formatFilesize($this->getFilesize(), $decimalPlaces, $includeTrailingZeroes); } public function getWidth() { return $this->width; } public function getHeight() { return $this->height; } public function getImgTagSizesString() { return sprintf('width="%d" height="%d"', $this->width, $this->height); } public function getType() { return $this->type; } public function getMimeType() { return $this->mimeType; } public function getResizedDimensions($maxWidth, $maxHeight, $currentWidth=null, $currentHeight=null) { $width = $this->width; $height = $this->height; if (isset($currentWidth)) $width = $currentWidth; if (isset($currentHeight)) $height = $currentHeight; $ar = $width / $height; if ($ar == 1) { //square $width = $height = min($maxWidth, $maxHeight); } elseif ($ar > ($maxWidth / $maxHeight)) { //Resize width $width = $maxWidth; $height = $maxWidth / $ar; } else { //Resize height $height = $maxHeight; $width = $maxHeight * $ar; } return array($width, $height); } public function getResizedImgTagSizesString($maxWidth, $maxHeight) { $width = $this->width; $height = $this->height; if (PHP_ROTATION) { switch ($this->getOrientation()) { case ExifData::ORIENTATION_FLIP_VERTICAL_ROTATE_270: case ExifData::ORIENTATION_ROTATE_270: case ExifData::ORIENTATION_FLIP_HORIZONTAL_ROTATE_270: case ExifData::ORIENTATION_ROTATE_90: $temp = $height; $height = $width; $width = $temp; break; } } list($w, $h) = $this->getResizedDimensions($maxWidth, $maxHeight, $width, $height); return sprintf('width="%d" height="%d"', $w, $h); } public function getThumbnail($maxWidth, $maxHeight, $return=false) { $cacheFilename = sys_get_temp_dir() . '/thumb_' . str_replace('/', '--', dirname(realpath($this->filename))) . "_{$this->getBasename(false)}.{$maxWidth}x{$maxHeight}.thumb"; if (!file_exists($cacheFilename) || filemtime($cacheFilename) < filemtime($this->filename)) { switch ($this->type) { case IMAGETYPE_JPEG: $in = imagecreatefromjpeg($this->filename); break; case IMAGETYPE_GIF: $in = imagecreatefromgif($this->filename); break; case IMAGETYPE_PNG: $in = imagecreatefrompng($this->filename); break; default: throw new Exception("Unable to handle image '{$this->filename}'"); } if (PHP_ROTATION) { $blk = imagecolorallocate($in, 0, 0, 0); switch ($this->getOrientation()) { case ExifData::ORIENTATION_STANDARD: break; case ExifData::ORIENTATION_FLIP_HORIZONTAL: // @todo break; case ExifData::ORIENTATION_ROTATE_180: $in = imagerotate($in, 180, $blk); break; case ExifData::ORIENTATION_FLIP_VERTICAL: // @todo break; case ExifData::ORIENTATION_FLIP_VERTICAL_ROTATE_270: // @todo $in = imagerotate($in, -90, $blk); break; case ExifData::ORIENTATION_ROTATE_270: $in = imagerotate($in, -90, $blk); break; case ExifData::ORIENTATION_FLIP_HORIZONTAL_ROTATE_270: // @todo $in = imagerotate($in, -90, $blk); break; case ExifData::ORIENTATION_ROTATE_90: $in = imagerotate($in, 90, $blk); break; } } list($w, $h) = $this->getResizedDimensions($maxWidth, $maxHeight, imagesx($in), imagesy($in)); $out = imagecreatetruecolor($w, $h); imagecopyresampled($out, $in, 0, 0, 0, 0, $w, $h, imagesx($in), imagesy($in)); imagejpeg($out, $cacheFilename, 80); imagedestroy($in); imagedestroy($out); } if ($return) { return file_get_contents($cacheFilename); } else { header("Content-Type: image/jpeg"); readfile($cacheFilename); } } public function getOrientation() { try { $exif = $this->getExifData(); return $exif->getOrientation(); } catch (NoExifDataException $ex) {} return 0; } public function getRotationDegrees($clockwise=false) { $deg = 0; switch ($this->getOrientation()) { case ExifData::ORIENTATION_FLIP_HORIZONTAL_ROTATE_270: case ExifData::ORIENTATION_FLIP_VERTICAL_ROTATE_270: case ExifData::ORIENTATION_ROTATE_270: $deg = 270; break; case ExifData::ORIENTATION_ROTATE_180: $deg = 180; break; case ExifData::ORIENTATION_ROTATE_90: $deg = 90; break; } if ($clockwise) { return 360-$deg; } return $deg; } public function getExifData() { return new ExifData($this->filename); } public function getExtraMetaData() { return array(); } } class VideoInfo implements IMediaInfo { private $filename; private $width; private $height; private $type; private $mimeType; private $ffmepg; public function __construct($filename) { $this->filename = $filename; $this->ffmpeg = new ffmpeg_movie($filename); $this->width = $this->ffmpeg->getFrameWidth(); $this->height = $this->ffmpeg->getFrameHeight(); $this->type = 0; $this->mimeType = 'video/avi'; } public function getFilename() { return $this->filename; } public function getBasename($stripExtension=false) { $info = pathinfo($this->filename); if ($stripExtension) { return $info['filename']; } else { return $info['basename']; } } public function getFilesize() { return filesize($this->filename); } public function getFilesizeHumanReadable($decimalPlaces=2, $includeTrailingZeroes=true) { return Formatting::formatFilesize($this->getFilesize(), $decimalPlaces, $includeTrailingZeroes); } public function getWidth() { return $this->width; } public function getHeight() { return $this->height; } public function getType() { return $this->type; } public function getMimeType() { return $this->mimeType; } public function getResizedDimensions($maxWidth, $maxHeight) { $ar = $this->width / $this->height; $width = $this->width; $height = $this->height; if ($ar == 1) { //square $width = $height = min($maxWidth, $maxHeight); } elseif ($ar > ($maxWidth / $maxHeight)) { //Resize width $width = $maxWidth; $height = $maxWidth / $ar; } else { //Resize height $height = $maxHeight; $width = $maxHeight * $ar; } return array($width, $height); } public function getResizedImgTagSizesString($maxWidth, $maxHeight) { list($w, $h) = $this->getResizedDimensions($maxWidth, $maxHeight); return sprintf('width="%d" height="%d"', $w, $h); } public function getThumbnail($maxWidth, $maxHeight, $return=false) { $cacheFilename = sys_get_temp_dir() . '/thumb_' . str_replace('/', '--', dirname(realpath($this->filename))) . "_{$this->getBasename(false)}.{$maxWidth}x{$maxHeight}.thumb"; if (!file_exists($cacheFilename) || filemtime($cacheFilename) < filemtime($this->filename)) { list($w, $h) = $this->getResizedDimensions($maxWidth, $maxHeight); $frame = $this->ffmpeg->getFrame(1); $in = $frame->toGDImage(); $out = imagecreatetruecolor($w, $h); imagecopyresampled($out, $in, 0, 0, 0, 0, $w, $h, $this->width, $this->height); imagejpeg($out, $cacheFilename, 80); imagedestroy($in); imagedestroy($out); } if ($return) { return file_get_contents($cacheFilename); } else { header("Content-Type: image/jpeg"); readfile($cacheFilename); } } public function getOrientation() { return 0; } public function getRotationDegrees($clockwise=false) { return 0; } public function getExifData() { throw new NoExifDataException(); } public function getExtraMetaData() { $out = array( 'Frame Rate' => $this->ffmpeg->getFrameRate() . 'fps', ); $d = round($this->ffmpeg->getDuration()); $h = $m = 0; $s = $d; if ($d >= 60) { $m = floor($d / 60); $s = $d % 60; if ($m >= 60) { $h = floor($m / 60); $m = $m % 60; } } $h = str_pad($h, 2, '0', STR_PAD_LEFT); $m = str_pad($m, 2, '0', STR_PAD_LEFT); $s = str_pad($s, 2, '0', STR_PAD_LEFT); $out['Duration'] = ltrim("$h:$m:$s", '0:') . ($d < 60 ? ' secs' : ''); if ($this->ffmpeg->hasVideo()) { $out['Video Codec'] = $this->ffmpeg->getVideoCodec(); } if ($this->ffmpeg->hasAudio()) { $out['Audio Codec'] = $this->ffmpeg->getAudioCodec(); } return $out; } } class Gallery { public static function createAllForDirectory($dir=null) { if ($dir === null) $dir = getcwd(); $out = array(); foreach (glob("$dir/*") as $ff) { if ($ff == __FILE__) continue; $fInfo = pathinfo($ff); if (isset($fInfo['extension']) && in_array($fInfo['extension'], array('avi','mkv'))) { //Video $out[] = new VideoInfo($ff); } else { //Image? if (!$img = getimagesize($ff)) continue; $out[] = new ImageInfo($ff, $img); } } return $out; } } function html($s) { return htmlspecialchars($s, ENT_NOQUOTES); } function htmlq($s) { return htmlspecialchars($s, ENT_QUOTES); } if (!function_exists('imagecreatefromjpeg')) { die("The 'gd' extension is not available."); } $startTime = microtime(true); @ob_start('ob_gzhandler', 1024); $files = glob('*'); if (isset($_GET['thumb'])) { if (in_array($_GET['thumb'], $files)) { $img = new ImageInfo($_GET['thumb']); if (isset($_GET['l'])) { $img->getThumbnail(1024, 1024); } else { $img->getThumbnail(200, 200); } die(); } else { die("Invalid image"); } } $images = Gallery::createAllForDirectory(); $singleFile = $prevImage = $nextImage = false; if (isset($_GET['file'])) { while (list($k, /** @var IMediaInfo */ $v) = each($images)) { if ($v->getBasename(false) == $_GET['file']) { $singleFile = $v; if (array_key_exists($k-1, $images)) { $prevImage = $images[$k-1]; } if (array_key_exists($k+1, $images)) { $nextImage = $images[$k+1]; } break; } } } if (isset($_GET['archive']) && in_array(strtolower($_GET['archive']), array('zip', 'tar'))) { // ZIP or TAR archive if (!$images) die(); $dir = dirname($images[0]->getFilename()); $filenames = array(); foreach ($images as /** @var IMediaInfo */ $i) { if (dirname($i->getFilename()) != $dir) { $filenames[] = escapeshellarg($i->getFilename()); } else { $filenames[] = escapeshellarg($i->getBasename()); } } $filenames = join(' ', $filenames); $archiveFilename = basename(getcwd()) . '.' . $_GET['archive']; header('Content-Disposition: attachment; filename="' . $archiveFilename . '"'); $curDir = getcwd(); chdir($dir); if (strcasecmp($_GET['archive'], 'zip') == 0) { header('Content-Type: application/zip'); passthru("zip - $filenames"); } elseif (strcasecmp($_GET['archive'], 'tar') == 0) { header('Content-Type: application/x-tar'); passthru("tar -c $filenames"); } chdir($curdir); die(); } ?> Gallery of <?=html(basename(dirname($_SERVER['PHP_SELF'])))?>!

getBasename(true))?>

getRotationDegrees(true) : 0); ?> <?=htmlq($singleFile->getBasename(true))?>
getExifData(); ?> getOrientation()) { ?> getExposure()) > 0.0001) { ?> getFNumber() != "f/1") { ?> getShutterSpeed() != "1 secs") { ?> getAperture() != "0.00") { ?> getFlash()) { ?> getFocalLength() != '0.00') { ?> getCoordinates(); ?> getExtraMetaData() as $k => $v) { ?>
EXIF Data
DimensionsgetWidth()?>xgetHeight()?>
Filesize:getFilesizeHumanReadable()?>
Make:getMake())?>
Model:getModel())?>
Orientation:getOrientation()?>
Date/Time:getDateTime())?>
Exposure:getExposure()?> secs
F-Number:getFNumber()?>
ISO Speed:getIsoSpeed()?>
Shutter Speed:getShutterSpeed()?>
Aperture:getAperture()?>
Flash:getFlash()?>
Focal Length:getFocalLength()?>
Location Name:getCoordinates()->queryLocationName()?>
Location Coordinates:getCoordinates()->toPrettyCoordinates()?>
Location Coordinates:getCoordinates()->toDecimalString()?>
:

Gallery of !