Image MetaData

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
mudgil.gaurav
Forum Newbie
Posts: 17
Joined: Wed Oct 08, 2008 4:39 am

Image MetaData

Post by mudgil.gaurav »

Hi All,

I want to extract the meta tag information of a image uploaded by the user.I gone through many articles and blogs
all suggested about the "EXIF" library.But the issue is my hosting provider not providing me any support to
enable any of the PHP directive/library.

So i suppose to do it by the coding side.Please suggest me the best approach for it.

Any help or suggestion would be greatly appreciated.

Thanks
Gaurav
User avatar
Darhazer
DevNet Resident
Posts: 1011
Joined: Thu May 14, 2009 3:00 pm
Location: HellCity, Bulgaria

Re: Image MetaData

Post by Darhazer »

some time ago I've wrote this to read JFIF data.
You can easly modify it for exif data.
EXIF is 0xFF 0xE1
Read about JPEG headers here

Code: Select all

class Util_ImageParser {

	protected static function get_parsed_http_headers($fp, $urlToLog)
	{
		$headers = array();
		$status = null;
		while ($line = fgets($fp)) {

			if (trim($line) == '') {
				break;
			}

			if ($status == null) {
				$status = 500; // this is to return error in case we cannot match the status
				$result = preg_match('#HTTP/1.[0-9] ([0-9]+)#', $line, $match);
				if ($result && isset($match[1])) {
					$status = $match[1];
				} else {
					Util_Logger::log('Cannot determine status from header line ' . $line .' ('.$urlToLog.')', false, 'external_image_size.log');
				}
				$headers['Status'] = $status;
			} else {
				list($name, $value) = explode(':', $line, 2);
				$headers[trim($name)] = trim($value);
			}
		}

		return $headers;
	}


	public static function parse($url, &$width, &$height,$redirectCount=0){

		// parse the url to construct http request via socket
		$urlToLog = $url;
		$url = parse_url($url);
		if ($url == false) {
			return false;
		}

		$scheme = $url['scheme'] == 'https' ? 'ssl://' : '';

		$host = $url['host'];
		$port = !empty($scheme) ? '443' : '80';

		$path = $url['path'];

		$pathParts = explode('/', $path);
		$pathParts = array_map('rawurldecode', $pathParts); // if path is already encoded, decode
		$pathParts = array_map('rawurlencode', $pathParts); // now encode it correcly
		$path = implode('/', $pathParts);

		if (isset($url['query']))
		$path .= '?' . $url['query'];

		$errno = $errstr = '';
		$fp = fsockopen($scheme . $host, $port, $errno, $errstr, 5);
		if ($fp == false) {
			return false;
		}

		// make http request
		fwrite($fp, "GET $path HTTP/1.1\r\n");
		fwrite($fp, "Host:$host\r\n");
		fwrite($fp, "Connection: Close\r\n\r\n");

		// parse the response headers
		$headers = self::get_parsed_http_headers($fp, $urlToLog);

		if ($headers['Status'] != 200) {
			fclose($fp);

			if (isset($headers['Location']) && $redirectCount < 10)
				return self::parse($headers['Location'], $width, $height, ++$redirectCount);

			return false;
		}

		// we need 6 bytes to identify GIF, 8 bytes to identify PNG and 10 bytes for JPG
		$type = fread($fp, 6);
		if ($type == false)
		return false;

		// GIF
		if ($type == 'GIF87a' || $type == 'GIF89a') {
			$width = bin2int(fread($fp, 2));
			$height = bin2int(fread($fp, 2));
			fclose($fp);
			return true;
		}

		// read 2 more bytes
		$bytes = fread($fp, 2);

		if ($bytes == false)
		return false;
		$type .= $bytes;

		// PNG
		if (bin2hex($type) == '89504e470d0a1a0a') {// PNG header: 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
			$bytesToSkip = fread($fp, 8);
			if ($bytesToSkip == false)
			return false;
			$width = bin2int(fread($fp, 4), true);
			$height = bin2int(fread($fp, 4), true);
			fclose($fp);
			return true;
		}

		// read 3 more bytes
		$bytes = fread($fp, 3);
		//$buffer .= $bytes;

		if ($bytes == false)
		return false;
		$type .= $bytes;

		$buffer = $type;
		//unset($type);
		// JPEG
		$i = 0;
		$block = $buffer;

		$jfifFound = false;
		$inJfif = false;
		$first = true;
		if (bin2hex($buffer[$i]) == 'ff' && bin2hex($buffer[$i+1]) == 'd8') // JPEG
		{
			$i+=2; // skip the marker
			do {
				$block_type = $buffer[$i] . $buffer[$i+1];
				$block_length = bin2int($buffer[$i+2]. $buffer[$i+3], true);

				if ($block_length == 0)
					return false;
				$type = $buffer[$i+4] . $buffer[$i+5] . $buffer[$i+6] . $buffer[$i+7];

				if (!$jfifFound && $type == 'JFIF') {
					$jfifFound = true;
				}
				//0xFFC0 and 0xFFC2 are the "Start of frame" markers for standard and progressive jpegs, which contains the file size
				if($jfifFound && bin2hex($block_type) == 'ffc0' || bin2hex($block_type) == 'ffc2') {
					// The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
					$height = bin2int($buffer[$i+5] . $buffer[$i+6], true);
					$width = bin2int($buffer[$i+7] . $buffer[$i+8], true);
					fclose($fp);
					return true;
				} elseif (bin2hex($buffer[$i]) == 'ff') {
					$i+=2;
				}

				$i+= $block_length;
				while (strlen($buffer) < $i+9) {
					$bytes = fread($fp, ($i+9) - strlen($buffer));

					if($bytes == false) {
						fclose($fp);
						return false;
					}
					$buffer .= $bytes;
					if (feof($fp)) {
						fclose($fp);
						return false;
					}
				}
			} while(!feof($fp));
		}
		fclose($fp);
		return false;
	}
}

	function bin2int($value, $bigEndian = false)
	{
		if (strlen($value) == 2) {
			$data = $bigEndian ? unpack('n', $value) : unpack('v', $value);
		} else {
			$data = $bigEndian ? unpack('N', $value) : unpack('V', $value);
		}

		return $data[1];
	}


Post Reply