function parseOlympus($block, &$result, $seek, $globalOffset) { if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } $model = $result['IFD0']['Model']; $place = 8; //current place $offset = 8; //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num); //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Olympus_tag($tag); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $v = fseek($seek, $globalOffset + hexdec($value)); //offsets are from TIFF header which is 12 bytes from the start of the file if ($v == 0) { $data = fread($seek, $bytesofdata); } else { if ($v == -1) { $result['Errors'] = $result['Errors']++; } } } $formated_data = formatOlympusData($type, $tag, $intel, $data); if ($result['VerboseOutput'] == 1) { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } }
function read_exif_data_raw($path, $verbose) { if ($path == '' || $path == 'none') { return; } $in = @fopen($path, 'rb'); // the b is for windows machines to open in binary mode $seek = @fopen($path, 'rb'); // There may be an elegant way to do this with one file handle. $globalOffset = 0; if (!isset($verbose)) { $verbose = 0; } $result['VerboseOutput'] = $verbose; $result['Errors'] = 0; if (!$in || !$seek) { // if the path was invalid, this error will catch it $result['Errors'] = 1; $result['Error'][$result['Errors']] = gettext('The file could not be found.'); return $result; } $GLOBALS['exiferFileSize'] = filesize($path); // First 2 bytes of JPEG are 0xFFD8 $data = bin2hex(fread($in, 2)); if ($data == 'ffd8') { $result['ValidJpeg'] = 1; } else { $result['ValidJpeg'] = 0; fseek($in, 0); } $result['ValidIPTCData'] = 0; $result['ValidJFIFData'] = 0; $result['ValidEXIFData'] = 0; $result['ValidAPP2Data'] = 0; $result['ValidCOMData'] = 0; if ($result['ValidJpeg'] == 1) { // Next 2 bytes are MARKER tag (0xFFE#) $data = bin2hex(fread($in, 2)); $size = bin2hex(fread($in, 2)); // LOOP THROUGH MARKERS TILL YOU GET TO FFE1 (exif marker) $abortCount = 0; while (!feof($in) && $data != 'ffe1' && $data != 'ffc0' && $data != 'ffd9' && ++$abortCount < 200) { if ($data == 'ffe0') { // JFIF Marker $result['ValidJFIFData'] = 1; $result['JFIF']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['JFIF']['Data'] = $data; } $result['JFIF']['Identifier'] = substr($data, 0, 5); $result['JFIF']['ExtensionCode'] = bin2hex(substr($data, 6, 1)); $globalOffset += hexdec($size) + 2; } else { if ($data == 'ffed') { // IPTC Marker $result['ValidIPTCData'] = 1; $result['IPTC']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['IPTC']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == 'ffe2') { // EXIF extension Marker $result['ValidAPP2Data'] = 1; $result['APP2']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['APP2']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == 'fffe') { // COM extension Marker $result['ValidCOMData'] = 1; $result['COM']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['COM']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == 'ffe1') { $result['ValidEXIFData'] = 1; } } } } } $data = bin2hex(fread($in, 2)); $size = bin2hex(fread($in, 2)); } // END MARKER LOOP if ($data == 'ffe1') { $result['ValidEXIFData'] = 1; } else { fclose($in); fclose($seek); return $result; } // Size of APP1 $result['APP1Size'] = hexdec($size); // Start of APP1 block starts with 'Exif' header (6 bytes) $header = fread($in, 6); } // END IF ValidJpeg // Then theres a TIFF header with 2 bytes of endieness (II or MM) $header = fread($in, 2); if ($header === 'II') { $intel = 1; $result['Endien'] = 'Intel'; } else { if ($header === 'MM') { $intel = 0; $result['Endien'] = 'Motorola'; } else { $intel = 1; // not sure what the default should be, but this seems reasonable $result['Endien'] = 'Unknown'; } } // 2 bytes of 0x002a $tag = bin2hex(fread($in, 2)); // Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header) $offset = bin2hex(fread($in, 4)); if ($intel == 1) { $offset = intel2Moto($offset); } // Check for extremely large values here if (hexdec($offset) > 100000) { $result['ValidEXIFData'] = 0; fclose($in); fclose($seek); return $result; } if (hexdec($offset) > 8) { $unknown = fread($in, hexdec($offset) - 8); } // fixed this bug in 1.3 // add 12 to the offset to account for TIFF header if ($result['ValidJpeg'] == 1) { $globalOffset += 12; } //=========================================================== // Start of IFD0 $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['IFD0NumTags'] = $num; if ($num < 1000) { // 1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, 'IFD0', $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = 'Illegal size for IFD0'; } // store offset to IFD1 $offset = bin2hex(fread($in, 4)); if ($intel == 1) { $offset = intel2Moto($offset); } $result['IFD1Offset'] = hexdec($offset); // Check for SubIFD if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) { fclose($in); fclose($seek); return $result; } // seek to SubIFD (Value of ExifOffset tag) above. $ExitOffset = $result['IFD0']['ExifOffset']; $v = fseek($in, $globalOffset + $ExitOffset); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Could not Find SubIFD'); } //=========================================================== // Start of SubIFD $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['SubIFDNumTags'] = $num; if ($num < 1000) { // 1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, 'SubIFD', $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Illegal size for SubIFD'); } // Add the 35mm equivalent focal length: if (isset($result['IFD0']['FocalLengthIn35mmFilm']) && !isset($result['SubIFD']['FocalLengthIn35mmFilm'])) { // found in the wrong place $result['SubIFD']['FocalLengthIn35mmFilm'] = $result['IFD0']['FocalLengthIn35mmFilm']; } if (!isset($result['SubIFD']['FocalLengthIn35mmFilm'])) { $result['SubIFD']['FocalLengthIn35mmFilm'] = get35mmEquivFocalLength($result); } // Check for IFD1 if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) { fclose($in); fclose($seek); return $result; } // seek to IFD1 $v = fseek($in, $globalOffset + $result['IFD1Offset']); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Could not Find IFD1'); } //=========================================================== // Start of IFD1 $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['IFD1NumTags'] = $num; if ($num < 1000) { // 1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, 'IFD1', $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Illegal size for IFD1'); } // If verbose output is on, include the thumbnail raw data... if ($result['VerboseOutput'] == 1 && $result['IFD1']['JpegIFOffset'] > 0 && $result['IFD1']['JpegIFByteCount'] > 0) { $v = fseek($seek, $globalOffset + $result['IFD1']['JpegIFOffset']); if ($v == 0) { $data = fread($seek, $result['IFD1']['JpegIFByteCount']); } else { if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; } } $result['IFD1']['ThumbnailData'] = $data; } // Check for Interoperability IFD if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) || $result['SubIFD']['ExifInteroperabilityOffset'] == 0) { fclose($in); fclose($seek); return $result; } // Seek to InteroperabilityIFD $v = fseek($in, $globalOffset + $result['SubIFD']['ExifInteroperabilityOffset']); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Could not Find InteroperabilityIFD'); } //=========================================================== // Start of InteroperabilityIFD $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['InteroperabilityIFDNumTags'] = $num; if ($num < 1000) { // 1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, 'InteroperabilityIFD', $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = gettext('Illegal size for InteroperabilityIFD'); } fclose($in); fclose($seek); return $result; }
function parseFujifilm($block, &$result) { //if($result['Endien']=="Intel") $intel=1; //else $intel=0; $intel = 1; $model = $result['IFD0']['Model']; $place = 8; //current place $offset = 8; $num = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['Offset'] = hexdec($num); //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num); //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Fujifilm_tag($tag); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $data = substr($block, hexdec($value) - $offset, $bytesofdata * 2); } $formated_data = formatFujifilmData($type, $tag, $intel, $data); if ($result['VerboseOutput'] == 1) { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } }
function parseGPS($block, &$result, $offset, $seek, $globalOffset) { if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } $v = fseek($seek, $globalOffset + $offset); //offsets are from TIFF header which is 12 bytes from the start of the file if ($v == -1) { $result['Errors'] = $result['Errors']++; } $num = bin2hex(fread($seek, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['GPS']['NumTags'] = $num; if ($num == 0) { return; } $block = fread($seek, $num * 12); $place = 0; //loop thru all tags Each field is 12 bytes for ($i = 0; $i < $num; $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_GPS_tag($tag); //2 byte datatype $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte number of elements $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value or pointer to value if larger than 4 bytes $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { if (strpos('unknown', $tag_name) !== false || $bytesofdata > 1024) { $result['Errors'] = $result['Errors']++; $data = ''; $type = 'ASCII'; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $v = fseek($seek, $globalOffset + hexdec($value)); //offsets are from TIFF header which is 12 bytes from the start of the file if ($v == 0) { $data = fread($seek, $bytesofdata); } else { $result['Errors'] = $result['Errors']++; $data = ''; $type = 'ASCII'; } } } if ($result['VerboseOutput'] == 1) { $result['GPS'][$tag_name] = formatGPSData($type, $tag, $intel, $data); $result['GPS'][$tag_name . "_Verbose"]['RawData'] = bin2hex($data); $result['GPS'][$tag_name . "_Verbose"]['Type'] = $type; $result['GPS'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['GPS'][$tag_name] = formatGPSData($type, $tag, $intel, $data); } } }
function parseCanon($block, &$result, $seek, $globalOffset) { $place = 0; //current place if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } $model = $result['IFD0']['Model']; //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num); //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Canon_tag($tag); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); if ($bytesofdata <= 0) { return; //if this value is 0 or less then we have read all the tags we can } //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $v = fseek($seek, $globalOffset + hexdec($value)); //offsets are from TIFF header which is 12 bytes from the start of the file if (isset($GLOBALS['exiferFileSize'])) { $exiferFileSize = $GLOBALS['exiferFileSize']; } else { $exiferFileSize = 0; } if ($v == 0 && $bytesofdata < $exiferFileSize) { $data = fread($seek, $bytesofdata); } else { if ($v == -1) { $result['Errors'] = $result['Errors']++; $data = ''; } else { $data = ''; } } } $result['SubIFD']['MakerNote'][$tag_name] = ''; // insure the index exists $formated_data = formatCanonData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]); if ($result['VerboseOutput'] == 1) { //$result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { //$result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } }
function parseNikon($block, &$result) { if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } $model = $result['IFD0']['Model']; //these 6 models start with "Nikon". Other models dont. if ($model == "E700" || $model == "E800" || $model == "E900" || $model == "E900S" || $model == "E910" || $model == "E950") { $place = 8; //current place $model = 0; //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num); //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Nikon_tag($tag, $model); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; //if tag is 0002 then its the ASCII value which we know is at 140 so calc offset //THIS HACK ONLY WORKS WITH EARLY NIKON MODELS if ($tag == "0002") { $offset = hexdec($value) - 140; } if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $data = substr($block, hexdec($value) - $offset, $bytesofdata * 2); } $formated_data = formatNikonData($type, $tag, $intel, $model, $data); if ($result['VerboseOutput'] == 1) { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } } else { $place = 0; //current place $model = 1; $nikon = substr($block, $place, 8); $place += 8; $endien = substr($block, $place, 4); $place += 4; //2 bytes of 0x002a $tag = bin2hex(substr($block, $place, 2)); $place += 2; //Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header) $offset = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $offset = intel2Moto($offset); } if (hexdec($offset) > 8) { $place += $offset - 8; } //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Nikon_tag($tag, $model); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $data = substr($block, hexdec($value) + hexdec($offset) + 2, $bytesofdata); } $formated_data = formatNikonData($type, $tag, $intel, $model, $data); if ($result['VerboseOutput'] == 1) { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } } }
function parseOlympus($block, &$result, $seek, $globalOffset) { if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } $model = $result['IFD0']['Model']; // New header for new DSLRs - Check for it because the // number of bytes that count the IFD fields differ in each case. // Fixed by Zenphoto 2/24/08 $new = false; if (substr($block, 0, 8) == "OLYMPUS") { $new = true; } else { if (substr($block, 0, 7) == "OLYMP" || substr($block, 0, 7) == "OLYMP") { $new = false; } else { // Header does not match known Olympus headers. // This is not a valid OLYMPUS Makernote. return false; } } // Offset of IFD entry after Olympus header. $place = 8; $offset = 8; // Get number of tags (1 or 2 bytes, depending on New or Old makernote) $countfieldbits = $new ? 1 : 2; // New makernote repeats 1-byte value twice, so increment $place by 2 in either case. $num = bin2hex(substr($block, $place, $countfieldbits)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $ntags = hexdec($num); $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = $ntags; //loop thru all tags Each field is 12 bytes for ($i = 0; $i < $ntags; $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Olympus_tag($tag); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } $v = fseek($seek, $globalOffset + hexdec($value)); //offsets are from TIFF header which is 12 bytes from the start of the file if (isset($GLOBALS['exiferFileSize']) && $v == 0 && $bytesofdata < $GLOBALS['exiferFileSize']) { $data = fread($seek, $bytesofdata); } else { $result['Errors'] = $result['Errors']++; $data = ''; } } $formated_data = formatOlympusData($type, $tag, $intel, $data); if ($result['VerboseOutput'] == 1) { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { $result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } }
function read_exif_data_raw($path, $verbose) { if ($path == '' || $path == 'none') { return; } $in = @fopen($path, "rb"); //the b is for windows machines to open in binary mode $seek = @fopen($path, "rb"); //There may be an elegant way to do this with one file handle. $globalOffset = 0; if (!isset($verbose)) { $verbose = 0; } $result['VerboseOutput'] = $verbose; $result['Errors'] = 0; if (!$in || !$seek) { //if the path was invalid, this error will catch it $result['Errors'] = 1; $result['Error'][$result['Errors']] = "The file could not be found."; return $result; } //First 2 bytes of JPEG are 0xFFD8 $data = bin2hex(fread($in, 2)); if ($data == "ffd8") { $result['ValidJpeg'] = 1; } else { $result['ValidJpeg'] = 0; fclose($in); fclose($seek); return $result; } $result['ValidIPTCData'] = 0; $result['ValidJFIFData'] = 0; $result['ValidEXIFData'] = 0; $result['ValidAPP2Data'] = 0; $result['ValidCOMData'] = 0; //Next 2 bytes are MARKER tag (0xFFE#) $data = bin2hex(fread($in, 2)); $size = bin2hex(fread($in, 2)); //LOOP THROUGH MARKERS TILL YOU GET TO FFE1 (exif marker) while (!feof($in) && $data != "ffe1" && $data != "ffc0" && $data != "ffd9") { if ($data == "ffe0") { //JFIF Marker $result['ValidJFIFData'] = 1; $result['JFIF']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['JFIF']['Data'] = $data; } $result['JFIF']['Identifier'] = substr($data, 0, 5); $result['JFIF']['ExtensionCode'] = bin2hex(substr($data, 6, 1)); $globalOffset += hexdec($size) + 2; } else { if ($data == "ffed") { //IPTC Marker $result['ValidIPTCData'] = 1; $result['IPTC']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['IPTC']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == "ffe2") { //EXIF extension Marker $result['ValidAPP2Data'] = 1; $result['APP2']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['APP2']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == "fffe") { //COM extension Marker $result['ValidCOMData'] = 1; $result['COM']['Size'] = hexdec($size); if (hexdec($size) - 2 > 0) { $data = fread($in, hexdec($size) - 2); $result['COM']['Data'] = $data; } $globalOffset += hexdec($size) + 2; } else { if ($data == "ffe1") { $result['ValidEXIFData'] = 1; } } } } } $data = bin2hex(fread($in, 2)); $size = bin2hex(fread($in, 2)); } //END MARKER LOOP if ($data == "ffe1") { $result['ValidEXIFData'] = 1; } else { fclose($in); fclose($seek); return $result; } //Size of APP1 $result['APP1Size'] = hexdec($size); //Start of APP1 block starts with "Exif" header (6 bytes) $header = fread($in, 6); //Then theres a TIFF header with 2 bytes of endieness (II or MM) $header = fread($in, 2); if ($header === "II") { $intel = 1; $result['Endien'] = "Intel"; } else { if ($header === "MM") { $intel = 0; $result['Endien'] = "Motorola"; } else { $intel = 1; //not sure what the default should be, but this seems reasonable $result['Endien'] = "Unknown"; } } //2 bytes of 0x002a $tag = bin2hex(fread($in, 2)); //Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header) $offset = bin2hex(fread($in, 4)); if ($intel == 1) { $offset = intel2Moto($offset); } // Check for extremely large values here if (hexdec($offset) > 100000) { $result['ValidEXIFData'] = 0; fclose($in); fclose($seek); return $result; } if (hexdec($offset) > 8) { $unknown = fread($in, hexdec($offset) - 8); } //fixed this bug in 1.3 //add 12 to the offset to account for TIFF header $globalOffset += 12; //===========================================================Start of IFD0 $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['IFD0NumTags'] = $num; if ($num < 1000) { //1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, "IFD0", $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Illegal size for IFD0"; } //store offset to IFD1 $offset = bin2hex(fread($in, 4)); if ($intel == 1) { $offset = intel2Moto($offset); } $result['IFD1Offset'] = hexdec($offset); //Check for SubIFD if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) { fclose($in); fclose($seek); return $result; } //seek to SubIFD (Value of ExifOffset tag) above. $ExitOffset = $result['IFD0']['ExifOffset']; $v = fseek($in, $globalOffset + $ExitOffset); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Couldnt Find SubIFD"; } //===========================================================Start of SubIFD $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['SubIFDNumTags'] = $num; if ($num < 1000) { //1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, "SubIFD", $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Illegal size for SubIFD"; } //Check for IFD1 if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) { fclose($in); fclose($seek); return $result; } //seek to IFD1 $v = fseek($in, $globalOffset + $result['IFD1Offset']); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Couldnt Find IFD1"; } //===========================================================Start of IFD1 $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['IFD1NumTags'] = $num; if ($num < 1000) { //1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, "IFD1", $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Illegal size for IFD1"; } //if verbose output is on, stick in the thumbnail raw data if ($result['VerboseOutput'] == 1 && $result['IFD1']['JpegIFOffset'] > 0 && $result['IFD1']['JpegIFByteCount'] > 0) { $v = fseek($seek, $globalOffset + $result['IFD1']['JpegIFOffset']); if ($v == 0) { $data = fread($seek, $result['IFD1']['JpegIFByteCount']); } else { if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; } } $result['IFD1']["ThumbnailData"] = $data; } //Check for Interoperability IFD if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) || $result['SubIFD']['ExifInteroperabilityOffset'] == 0) { fclose($in); fclose($seek); return $result; } //seek to InteroperabilityIFD $v = fseek($in, $globalOffset + $result['SubIFD']['ExifInteroperabilityOffset']); if ($v == -1) { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Couldnt Find InteroperabilityIFD"; } //===========================================================Start of InteroperabilityIFD $num = bin2hex(fread($in, 2)); if ($intel == 1) { $num = intel2Moto($num); } $num = hexdec($num); $result['InteroperabilityIFDNumTags'] = $num; if ($num < 1000) { //1000 entries is too much and is probably an error. for ($i = 0; $i < $num; $i++) { read_entry($result, $in, $seek, $intel, "InteroperabilityIFD", $globalOffset); } } else { $result['Errors'] = $result['Errors'] + 1; $result['Error'][$result['Errors']] = "Illegal size for InteroperabilityIFD"; } fclose($in); fclose($seek); return $result; }
function parseCanon($block, &$result, $seek, $globalOffset) { global $exiferFileSize; //Manuel: this makes canon maker notes work $place = 0; //current place if ($result['Endien'] == "Intel") { $intel = 1; } else { $intel = 0; } /* Manuel: start determine a possible makernote offset given in the makernote trailer */ $offsetDelta = 0; //currentOffset of MakerNote starting after TIFF header $blockLenBytes = strlen(bin2hex($block)) / 2; $currentOffset = ftell($seek) - $blockLenBytes - $globalOffset; //get potential trailer 8bytes from end of MakerNote //first 4 bytes are trailer signature corresponding to TIFF header $trailerId = bin2hex(substr($block, -8, 4)); if ($intel) { $tiffHeader = bin2hex('II'); //Intel // 2 bytes of 0x002a $tiffHeader .= intel2Moto('002a'); } else { $tiffHeader = bin2hex('MM'); //Motorola $tiffHeader .= '002a'; } if ($trailerId == $tiffHeader) { //is it the trailer? //next 4 bytes contain offset value $mnOffset = bin2hex(substr($block, -4)); if ($intel == 1) { $mnOffset = intel2Moto($mnOffset); } //calculate the delta $offsetDelta = $currentOffset - hexdec($mnOffset); } /* Manuel: end determine makernote offset */ $model = $result['IFD0']['Model']; //Get number of tags (2 bytes) $num = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $num = intel2Moto($num); } $result['SubIFD']['MakerNote']['MakerNoteNumTags'] = hexdec($num); //loop thru all tags Each field is 12 bytes for ($i = 0; $i < hexdec($num); $i++) { //2 byte tag $tag = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $tag = intel2Moto($tag); } $tag_name = lookup_Canon_tag($tag); //2 byte type $type = bin2hex(substr($block, $place, 2)); $place += 2; if ($intel == 1) { $type = intel2Moto($type); } lookup_type($type, $size); //4 byte count of number of data units $count = bin2hex(substr($block, $place, 4)); $place += 4; if ($intel == 1) { $count = intel2Moto($count); } $bytesofdata = $size * hexdec($count); if ($bytesofdata <= 0) { return; //if this value is 0 or less then we have read all the tags we can } //4 byte value of data or pointer to data $value = substr($block, $place, 4); $place += 4; if ($bytesofdata <= 4) { $data = $value; } else { $value = bin2hex($value); if ($intel == 1) { $value = intel2Moto($value); } //offsets are from TIFF header which is 12 bytes from the start of the file //Manuel: also account for $offsetDelta given by the makernote offset in TIFF trailer $v = fseek($seek, $globalOffset + hexdec($value) + $offsetDelta); if ($v == 0 && $bytesofdata < $exiferFileSize) { //Manuel: this makes canon maker notes work $data = fread($seek, $bytesofdata); } else { if ($v == -1) { $result['Errors'] = $result['Errors']++; } } } $formated_data = formatCanonData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]); if ($result['VerboseOutput'] == 1) { //$result['SubIFD']['MakerNote'][$tag_name] = $formated_data; if ($type == "URATIONAL" || $type == "SRATIONAL" || $type == "USHORT" || $type == "SSHORT" || $type == "ULONG" || $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") { $data = bin2hex($data); if ($intel == 1) { $data = intel2Moto($data); } } $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['RawData'] = $data; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Type'] = $type; $result['SubIFD']['MakerNote'][$tag_name . "_Verbose"]['Bytes'] = $bytesofdata; } else { //$result['SubIFD']['MakerNote'][$tag_name] = $formated_data; } } }