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];
}