--- /dev/null
+<?php
+/**
+ * General class for fetching and parsing EXIF information from images.
+ *
+ * Works equally well with either the built in php exif functions (if PHP
+ * compiled with exif support) or the (slower) bundled exif library.
+ *
+ * Copyright 2003-2009 The Horde Project (http://www.horde.org/)
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Horde_Image_Exif
+{
+ static public function factory($driver = null)
+ {
+ return new Horde_Image_Exif_Php();
+ }
+
+ /**
+ * Converts from Intel to Motorola endien. Just reverses the bytes
+ * (assumes hex is passed in)
+ *
+ * @param $num
+ * @return unknown_type
+ */
+ static public function Horde_Image_Exif::intel2Moto($num)
+ {
+ $len = strlen($intel);
+ $moto = '';
+ for($i = 0; $i <= $len; $i += 2) {
+ $moto .= substr($intel, $len-$i, 2);
+ }
+
+ return $moto;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ *
+ */
+abstract class Horde_Image_Exif_Base
+{
+ abstract public function getData($image);
+
+ /**
+ *
+ * @return unknown_type
+ */
+ static public function getFields()
+ {
+ return array('Make' => array('description' => _("Camera Make"), 'type' => 'text'),
+ 'Model' => array('description' => _("Camera Model"), 'type' => 'text'),
+ 'ImageType' => array('description' => _("Photo Type"), 'type' => 'text'),
+ 'ImageDescription' => array('description' => _("Photo Description"), 'type' => 'text'),
+ 'FileSize' => array('description' => _("File Size"), 'type' => 'number'),
+ 'DateTime' => array('description' => _("Date Photo Modified"), 'type' => 'date'),
+ 'DateTimeOriginal' => array('description' => _("Date Photo Taken"), 'type' => 'date'),
+ 'DateTimeDigitized' => array('description' => _("Date Photo Digitized"), 'type' => 'date'),
+ 'ExifImageWidth' => array('description' => _("Width"), 'type' => 'number'),
+ 'ExifImageLength' => array('description' => _("Height"), 'type' => 'number'),
+ 'XResolution' => array('description' => _("X Resolution"), 'type' => 'number'),
+ 'YResolution' => array('description' => _("Y Resolution"), 'type' => 'number'),
+ 'ResolutionUnit' => array('description' => _("Resolution Unit"), 'type' => 'text'),
+ 'ShutterSpeedValue' => array('description' => _("Shutter Speed"), 'type' => 'number'),
+ 'ExposureTime' => array('description' => _("Exposure"), 'type' => 'number'),
+ 'FocalLength' => array('description' => _("Focal Length"), 'type' => 'number'),
+ 'FocalLengthIn35mmFilm' => array('description' => _("Focal Length (35mm equiv)"), 'type' => 'number'),
+ 'ApertureValue' => array('description' => _("Aperture"), 'type' => 'number'),
+ 'FNumber' => array('description' => _("F-Number"), 'type' => 'number'),
+ 'ISOSpeedRatings' => array('description' => _("ISO Setting"), 'type' => 'number'),
+ 'ExposureBiasValue' => array('description' => _("Exposure Bias"), 'type' => 'number'),
+ 'ExposureMode' => array('description' => _("Exposure Mode"), 'type' => 'number'),
+ 'ExposureProgram' => array('description' => _("Exposure Program"), 'type' => 'number'),
+ 'MeteringMode' => array('description' => _("Metering Mode"), 'type' => 'number'),
+ 'Flash' => array('description' => _("Flash Setting"), 'type' => 'number'),
+ 'UserComment' => array('description' => _("User Comment"), 'type' => 'text'),
+ 'ColorSpace' => array('description' => _("Color Space"), 'type' => 'number'),
+ 'SensingMethod' => array('description' => _("Sensing Method"), 'type' => 'number'),
+ 'WhiteBalance' => array('description' => _("White Balance"), 'type' => 'number'),
+ 'Orientation' => array('description' => _("Camera Orientation"), 'type' => 'number'),
+ 'Copyright' => array('description' => _("Copyright"), 'type' => 'text'),
+ 'Artist' => array('description' => _("Artist"), 'type' => 'text'),
+ 'GPSLatitude' => array('description' => _("Latitude"), 'type' => 'gps'),
+ 'GPSLongitude' => array('description' => _("Longitude"), 'type' => 'gps'),
+ 'LightSource' => array('description' => _("Light source"), 'type' => 'number'),
+ 'FileSource' => array('description' => _("File source"), 'type' => 'number'),
+ );
+ }
+
+ /**
+ *
+ * @param $exif
+ * @return unknown_type
+ */
+ protected function _processData($exif)
+ {
+ $results = array();
+ if ($exif) {
+ $fields = self::getFields();
+
+ foreach ($fields as $field => $data) {
+ $value = isset($exif[$field]) ? $exif[$field] : '';
+ // Don't store empty fields.
+ if ($value === '') {
+ continue;
+ }
+
+ /* Special handling of GPS data */
+ if ($data['type'] == 'gps') {
+ $value = self::_parseGPSData($exif[$field]);
+ if (!empty($exif[$field . 'Ref']) && ($exif[$field . 'Ref'] == 'S' || $exif[$field . 'Ref'] == 'W')) {
+ $value = '-' . $value;
+ }
+ }
+
+ /* Date fields are converted to a timestamp.*/
+ if ($data['type'] == 'date') {
+ @list($ymd, $hms) = explode(' ', $value, 2);
+ @list($year, $month, $day) = explode(':', $ymd, 3);
+ if (!empty($hms) && !empty($year) && !empty($month) && !empty($day)) {
+ $time = "$month/$day/$year $hms";
+ $value = strtotime($time);
+ }
+ }
+
+ $results[$field] = $value;
+ }
+ }
+ }
+
+ /**
+ * Parse the Longitude and Latitude values into a standardized format
+ * regardless of the source format.
+ *
+ * @param mixed $data An array containing degrees, minutes, seconds
+ * in index 0, 1, 2 respectifully.
+ *
+ * @return double The location data in a decimal format.
+ */
+ protected function _parseGPSData($data)
+ {
+ // According to EXIF standard, GPS data can be in the form of
+ // dd/1 mm/1 ss/1 or as a decimal reprentation.
+ if ($data[0] == 0) {
+ return 0;
+ }
+ $min = explode('/', $data[1]);
+ if (count($min) > 1) {
+ $min = $min[0] / $min[1];
+ } else {
+ $min = $min[0];
+ }
+
+ $sec = explode('/', $data[2]);
+ if (count($sec) > 1) {
+ $sec = $sec[0] / $sec[1];
+ } else {
+ $sec = $sec[0];
+ }
+
+ return self::_degToDecimal($data[0], $min, $sec);
+ }
+
+ /**
+ *
+ * @param $degrees
+ * @param $minutes
+ * @param $seconds
+ * @return unknown_type
+ */
+ protected function _degToDecimal($degrees, $minutes, $seconds)
+ {
+ $degs = (double)($degrees + ($minutes / 60) + ($seconds/3600));
+ return round($degs, 6);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Class for dealing with Exif data using a bundled PHP library based on Exifer.
+ *
+ *
+ */
+class Horde_Image_Exif_Bundled extends Horde_Image_Exif_Base
+{
+ public function getData($image)
+ {
+ $raw = self::_exif_read_data($imageFile);
+ $exif = array();
+ foreach ($raw as $key => $value) {
+ if (($key == 'IFD0') || ($key == 'SubIFD')) {
+ foreach ($value as $subkey => $subvalue) {
+ $exif[$subkey] = $subvalue;
+ }
+ } else {
+ $exif[$key] = $value;
+ }
+ }
+ // Not really an EXIF property, but an attribute nonetheless...
+ // PHP's exif functions return it, so add it here to be consistent.
+ $exif['FileSize'] = @filesize($imageFile);
+
+ return $this->_processData($exif);
+ }
+
+
+ /**
+ *
+ * @param $path
+ * @return unknown_type
+ */
+ static protected function _exif_read_data($path)
+ {
+ if ($path == '' || $path == 'none') {
+ return;
+ }
+
+ // the b is for windows machines to open in binary mode
+ $in = @fopen($path, 'rb');
+
+ // There may be an elegant way to do this with one file handle.
+ $seek = @fopen($path, 'rb');
+ $globalOffset = 0;
+ $result['Errors'] = 0;
+
+ // if the path was invalid, this error will catch it
+ if (!$in || !$seek) {
+ $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;
+
+ } elseif ($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;
+
+ } elseif ($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;
+
+ } elseif ($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';
+ } elseif ($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 = Horde_Image_Exif::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);
+ }
+
+ // add 12 to the offset to account for TIFF header
+ $globalOffset += 12;
+
+ //===========================================================
+ // Start of IFD0
+ $num = bin2hex(fread($in, 2));
+ if ($intel == 1) {
+ $num = Horde_Image_Exif::intel2Moto($num);
+ }
+ $num = hexdec($num);
+ $result['IFD0NumTags'] = $num;
+
+ // 1000 entries is too much and is probably an error.
+ if ($num < 1000) {
+ for($i = 0; $i < $num; $i++) {
+ self::_readEntry($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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($num);
+ }
+ $num = hexdec($num);
+ $result['SubIFDNumTags'] = $num;
+
+ // 1000 entries is too much and is probably an error.
+ if ($num < 1000) {
+ for($i = 0; $i < $num; $i++) {
+ self::_readEntry($result, $in, $seek, $intel, 'SubIFD', $globalOffset);
+ }
+ } else {
+ $result['Errors'] = $result['Errors'] + 1;
+ $result['Error'][$result['Errors']] = _("Illegal size for SubIFD");
+ }
+
+ // Add the 35mm equivalent focal length:
+ // Now properly get this using the FocalLength35mmFilm tag
+ //$result['SubIFD']['FocalLength35mmEquiv'] = 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']] = _("Couldnt Find IFD1");
+ }
+
+ //===========================================================
+ // Start of IFD1
+ $num = bin2hex(fread($in, 2));
+ if ($intel == 1) {
+ $num = Horde_Image_Exif::intel2Moto($num);
+ }
+ $num = hexdec($num);
+ $result['IFD1NumTags'] = $num;
+
+ // 1000 entries is too much and is probably an error.
+ if ($num < 1000) {
+ for($i = 0; $i < $num; $i++) {
+ self::_readEntry($result, $in, $seek, $intel, 'IFD1', $globalOffset);
+ }
+ } else {
+ $result['Errors'] = $result['Errors'] + 1;
+ $result['Error'][$result['Errors']] = _("Illegal size for IFD1");
+ }
+ // include the thumbnail raw data...
+ if ($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 = Horde_Image_Exif::intel2Moto($num);
+ }
+ $num = hexdec($num);
+ $result['InteroperabilityIFDNumTags'] = $num;
+
+ // 1000 entries is too much and is probably an error.
+ if ($num < 1000) {
+ for($i = 0; $i < $num; $i++) {
+ self::_readEntry($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;
+ }
+
+ /**
+ *
+ * @param $result
+ * @param $in
+ * @param $seek
+ * @param $intel
+ * @param $ifd_name
+ * @param $globalOffset
+ * @return unknown_type
+ */
+ static protected function _readEntry(&$result, $in, $seek, $intel, $ifd_name, $globalOffset)
+ {
+ // Still ok to read?
+ if (feof($in)) {
+ $result['Errors'] = $result['Errors'] + 1;
+ return;
+ }
+
+ // 2 byte tag
+ $tag = bin2hex(fread($in, 2));
+ if ($intel == 1) $tag = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag);
+
+ // 2 byte datatype
+ $type = bin2hex(fread($in, 2));
+ if ($intel == 1) $type = Horde_Image_Exif::intel2Moto($type);
+ lookup_type($type, $size);
+
+ // 4 byte number of elements
+ $count = bin2hex(fread($in, 4));
+ if ($intel == 1) $count = Horde_Image_Exif::intel2Moto($count);
+ $bytesofdata = $size * hexdec($count);
+
+ // 4 byte value or pointer to value if larger than 4 bytes
+ $value = fread($in, 4 );
+
+ // if datatype is 4 bytes or less, its the value
+ if ($bytesofdata <= 4) {
+ $data = $value;
+ } elseif ($bytesofdata < 100000) {
+ // otherwise its a pointer to the value, so lets go get it
+ $value = bin2hex($value);
+ if ($intel == 1) {
+ $value = Horde_Image_Exif::intel2Moto($value);
+ }
+ // offsets are from TIFF header which is 12 bytes from the start of file
+ $v = fseek($seek, $globalOffset+hexdec($value));
+ if ($v == 0) {
+ $data = fread($seek, $bytesofdata);
+ } elseif ($v == -1) {
+ $result['Errors'] = $result['Errors'] + 1;
+ }
+ } else {
+ // bytesofdata was too big, so the exif had an error
+ $result['Errors'] = $result['Errors'] + 1;
+ return;
+ }
+
+ // if its a maker tag, we need to parse this specially
+ if ($tag_name == 'MakerNote') {
+ $make = $result['IFD0']['Make'];
+ if (strpos(strtolower($make), 'nikon') !== false) {
+ Horde_Image_Exif_Parser_Nikon::parse($data, $result);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } elseif (strpos(strtolower($make), 'olympus') !== false) {
+ Horde_Image_Exif_Parser_Olympus::parse($data, $result, $seek, $globalOffset);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } elseif (strpos(strtolower($make), 'canon') !== false) {
+ Horde_Image_Exif_Parser_Canon::parse($data, $result, $seek, $globalOffset);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } elseif (strpos(strtolower($make), 'fujifilm') !== false) {
+ Horde_Image_Exif_Parser_Fujifilm::parse($data, $result);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } elseif (strpos(strtolower($make), 'sanyo') !== false) {
+ Horde_Image_Exif_Parser_Sanyo::parse($data, $result, $seek, $globalOffset);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } elseif (strpos(strtolower($make), 'panasonic') !== false) {
+ Horde_Image_Exif_Parser_Panasonic::parse($data, $result, $seek, $globalOffset);
+ $result[$ifd_name]['KnownMaker'] = 1;
+ } else {
+ $result[$ifd_name]['KnownMaker'] = 0;
+ }
+ } elseif ($tag_name == 'GPSInfoOffset') {
+ $formated_data = self::_formatData($type, $tag, $intel, $data);
+ $result[$ifd_name]['GPSInfo'] = $formated_data;
+ Horde_Image_Exif_Parser_Gps::parse($data, $result, $formated_data, $seek, $globalOffset);
+ } else {
+ // Format the data depending on the type and tag
+ $formated_data = self::_formatData($type, $tag, $intel, $data);
+ $result[$ifd_name][$tag_name] = $formated_data;
+ }
+ }
+
+ /**
+ *
+ * @param $tag
+ * @return unknown_type
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch($tag)
+ {
+ // used by IFD0 'Camera Tags'
+ case '000b': $tag = 'ACDComment'; break; // text string up to 999 bytes long
+ case '00fe': $tag = 'ImageType'; break; // integer -2147483648 to 2147483647
+ case '0106': $tag = 'PhotometricInterpret'; break; // ?? Please send sample image with this tag
+ case '010e': $tag = 'ImageDescription'; break; // text string up to 999 bytes long
+ case '010f': $tag = 'Make'; break; // text string up to 999 bytes long
+ case '0110': $tag = 'Model'; break; // text string up to 999 bytes long
+ case '0112': $tag = 'Orientation'; break; // integer values 1-9
+ case '0115': $tag = 'SamplePerPixel'; break; // integer 0-65535
+ case '011a': $tag = 'xResolution'; break; // positive rational number
+ case '011b': $tag = 'yResolution'; break; // positive rational number
+ case '011c': $tag = 'PlanarConfig'; break; // integer values 1-2
+ case '0128': $tag = 'ResolutionUnit'; break; // integer values 1-3
+ case '0131': $tag = 'Software'; break; // text string up to 999 bytes long
+ case '0132': $tag = 'DateTime'; break; // YYYY:MM:DD HH:MM:SS
+ case '013b': $tag = 'Artist'; break; // text string up to 999 bytes long
+ case '013c': $tag = 'HostComputer'; break; // text string
+ case '013e': $tag = 'WhitePoint'; break; // two positive rational numbers
+ case '013f': $tag = 'PrimaryChromaticities'; break; // six positive rational numbers
+ case '0211': $tag = 'YCbCrCoefficients'; break; // three positive rational numbers
+ case '0213': $tag = 'YCbCrPositioning'; break; // integer values 1-2
+ case '0214': $tag = 'ReferenceBlackWhite'; break; // six positive rational numbers
+ case '8298': $tag = 'Copyright'; break; // text string up to 999 bytes long
+ case '8649': $tag = 'PhotoshopSettings'; break; // ??
+ case '8825': $tag = 'GPSInfoOffset'; break;
+ case '9286': $tag = 'UserCommentOld'; break; // ??
+ case '8769': $tag = 'ExifOffset'; break; // positive integer
+ // used by Exif SubIFD 'Image Tags'
+ case '829a': $tag = 'ExposureTime'; break; // seconds or fraction of seconds 1/x
+ case '829d': $tag = 'FNumber'; break; // positive rational number
+ case '8822': $tag = 'ExposureProgram'; break; // integer value 1-9
+ case '8824': $tag = 'SpectralSensitivity'; break; // ??
+ case '8827': $tag = 'ISOSpeedRatings'; break; // integer 0-65535
+ case '9000': $tag = 'ExifVersion'; break; // ??
+ case '9003': $tag = 'DateTimeOriginal'; break; // YYYY:MM:DD HH:MM:SS
+ case '9004': $tag = 'DateTimedigitized'; break; // YYYY:MM:DD HH:MM:SS
+ case '9101': $tag = 'ComponentsConfiguration'; break; // ??
+ case '9102': $tag = 'CompressedBitsPerPixel'; break; // positive rational number
+ case '9201': $tag = 'ShutterSpeedValue'; break; // seconds or fraction of seconds 1/x
+ case '9202': $tag = 'ApertureValue'; break; // positive rational number
+ case '9203': $tag = 'BrightnessValue'; break; // positive rational number
+ case '9204': $tag = 'ExposureBiasValue'; break; // positive rational number (EV)
+ case '9205': $tag = 'MaxApertureValue'; break; // positive rational number
+ case '9206': $tag = 'SubjectDistance'; break; // positive rational number (meters)
+ case '9207': $tag = 'MeteringMode'; break; // integer 1-6 and 255
+ case '9208': $tag = 'LightSource'; break; // integer 1-255
+ case '9209': $tag = 'Flash'; break; // integer 1-255
+ case '920a': $tag = 'FocalLength'; break; // positive rational number (mm)
+ case '9213': $tag = 'ImageHistory'; break; // text string up to 999 bytes long
+ case '927c': $tag = 'MakerNote'; break; // a bunch of data
+ case '9286': $tag = 'UserComment'; break; // text string
+ case '9290': $tag = 'SubsecTime'; break; // text string up to 999 bytes long
+ case '9291': $tag = 'SubsecTimeOriginal'; break; // text string up to 999 bytes long
+ case '9292': $tag = 'SubsecTimeDigitized'; break; // text string up to 999 bytes long
+ case 'a000': $tag = 'FlashPixVersion'; break; // ??
+ case 'a001': $tag = 'ColorSpace'; break; // values 1 or 65535
+ case 'a002': $tag = 'ExifImageWidth'; break; // ingeter 1-65535
+ case 'a003': $tag = 'ExifImageHeight'; break; // ingeter 1-65535
+ case 'a004': $tag = 'RelatedSoundFile'; break; // text string 12 bytes long
+ case 'a005': $tag = 'ExifInteroperabilityOffset'; break; // positive integer
+ case 'a20c': $tag = 'SpacialFreqResponse'; break; // ??
+ case 'a20b': $tag = 'FlashEnergy'; break; // positive rational number
+ case 'a20e': $tag = 'FocalPlaneXResolution'; break; // positive rational number
+ case 'a20f': $tag = 'FocalPlaneYResolution'; break; // positive rational number
+ case 'a210': $tag = 'FocalPlaneResolutionUnit'; break; // values 1-3
+ case 'a214': $tag = 'SubjectLocation'; break; // two integers 0-65535
+ case 'a215': $tag = 'ExposureIndex'; break; // positive rational number
+ case 'a217': $tag = 'SensingMethod'; break; // values 1-8
+ case 'a300': $tag = 'FileSource'; break; // integer
+ case 'a301': $tag = 'SceneType'; break; // integer
+ case 'a302': $tag = 'CFAPattern'; break; // undefined data type
+ case 'a401': $tag = 'CustomerRender'; break; // values 0 or 1
+ case 'a402': $tag = 'ExposureMode'; break; // values 0-2
+ case 'a403': $tag = 'WhiteBalance'; break; // values 0 or 1
+ case 'a404': $tag = 'DigitalZoomRatio'; break; // positive rational number
+ case 'a405': $tag = 'FocalLengthIn35mmFilm';break;
+ case 'a406': $tag = 'SceneCaptureMode'; break; // values 0-3
+ case 'a407': $tag = 'GainControl'; break; // values 0-4
+ case 'a408': $tag = 'Contrast'; break; // values 0-2
+ case 'a409': $tag = 'Saturation'; break; // values 0-2
+ case 'a40a': $tag = 'Sharpness'; break; // values 0-2
+
+ // used by Interoperability IFD
+ case '0001': $tag = 'InteroperabilityIndex'; break; // text string 3 bytes long
+ case '0002': $tag = 'InteroperabilityVersion'; break; // datatype undefined
+ case '1000': $tag = 'RelatedImageFileFormat'; break; // text string up to 999 bytes long
+ case '1001': $tag = 'RelatedImageWidth'; break; // integer in range 0-65535
+ case '1002': $tag = 'RelatedImageLength'; break; // integer in range 0-65535
+
+ // used by IFD1 'Thumbnail'
+ case '0100': $tag = 'ImageWidth'; break; // integer in range 0-65535
+ case '0101': $tag = 'ImageLength'; break; // integer in range 0-65535
+ case '0102': $tag = 'BitsPerSample'; break; // integers in range 0-65535
+ case '0103': $tag = 'Compression'; break; // values 1 or 6
+ case '0106': $tag = 'PhotometricInterpretation'; break;// values 0-4
+ case '010e': $tag = 'ThumbnailDescription'; break; // text string up to 999 bytes long
+ case '010f': $tag = 'ThumbnailMake'; break; // text string up to 999 bytes long
+ case '0110': $tag = 'ThumbnailModel'; break; // text string up to 999 bytes long
+ case '0111': $tag = 'StripOffsets'; break; // ??
+ case '0112': $tag = 'ThumbnailOrientation'; break; // integer 1-9
+ case '0115': $tag = 'SamplesPerPixel'; break; // ??
+ case '0116': $tag = 'RowsPerStrip'; break; // ??
+ case '0117': $tag = 'StripByteCounts'; break; // ??
+ case '011a': $tag = 'ThumbnailXResolution'; break; // positive rational number
+ case '011b': $tag = 'ThumbnailYResolution'; break; // positive rational number
+ case '011c': $tag = 'PlanarConfiguration'; break; // values 1 or 2
+ case '0128': $tag = 'ThumbnailResolutionUnit'; break; // values 1-3
+ case '0201': $tag = 'JpegIFOffset'; break;
+ case '0202': $tag = 'JpegIFByteCount'; break;
+ case '0212': $tag = 'YCbCrSubSampling'; break;
+
+ // misc
+ case '00ff': $tag = 'SubfileType'; break;
+ case '012d': $tag = 'TransferFunction'; break;
+ case '013d': $tag = 'Predictor'; break;
+ case '0142': $tag = 'TileWidth'; break;
+ case '0143': $tag = 'TileLength'; break;
+ case '0144': $tag = 'TileOffsets'; break;
+ case '0145': $tag = 'TileByteCounts'; break;
+ case '014a': $tag = 'SubIFDs'; break;
+ case '015b': $tag = 'JPEGTables'; break;
+ case '828d': $tag = 'CFARepeatPatternDim'; break;
+ case '828e': $tag = 'CFAPattern'; break;
+ case '828f': $tag = 'BatteryLevel'; break;
+ case '83bb': $tag = 'IPTC/NAA'; break;
+ case '8773': $tag = 'InterColorProfile'; break;
+
+ case '8828': $tag = 'OECF'; break;
+ case '8829': $tag = 'Interlace'; break;
+ case '882a': $tag = 'TimeZoneOffset'; break;
+ case '882b': $tag = 'SelfTimerMode'; break;
+ case '920b': $tag = 'FlashEnergy'; break;
+ case '920c': $tag = 'SpatialFrequencyResponse'; break;
+ case '920d': $tag = 'Noise'; break;
+ case '9211': $tag = 'ImageNumber'; break;
+ case '9212': $tag = 'SecurityClassification'; break;
+ case '9214': $tag = 'SubjectLocation'; break;
+ case '9215': $tag = 'ExposureIndex'; break;
+ case '9216': $tag = 'TIFF/EPStandardID'; break;
+ case 'a20b': $tag = 'FlashEnergy'; break;
+
+ default: $tag = 'unknown:'.$tag; break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ *
+ * @param $type
+ * @param $tag
+ * @param $intel
+ * @param $data
+ * @return unknown_type
+ */
+ static protected function _formatData($type, $tag, $intel, $data)
+ {
+ if ($type == 'ASCII') {
+ // Search for a null byte and stop there.
+ if (($pos = strpos($data, chr(0))) !== false) {
+ $data = substr($data, 0, $pos);
+ }
+ // Format certain kinds of strings nicely (Camera make etc.)
+ if ($tag == '010f') {
+ $data = ucwords(strtolower(trim($data)));
+ }
+
+ } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ if ($intel == 1) {
+ $top = hexdec(substr($data,8,8)); // intel stores them bottom-top
+ } else {
+ $top = hexdec(substr($data,0,8)); // motorola stores them top-bottom
+ }
+
+ if ($intel == 1) {
+ $bottom = hexdec(substr($data,0,8)); // intel stores them bottom-top
+ } else {
+ $bottom = hexdec(substr($data,8,8)); // motorola stores them top-bottom
+ }
+
+ if ($type == 'SRATIONAL' && $top > 2147483647) {
+ // this makes the number signed instead of unsigned
+ $top = $top - 4294967296;
+ }
+ if ($bottom != 0) {
+ $data = $top / $bottom;
+ } elseif ($top == 0) {
+ $data = 0;
+ } else {
+ $data = $top . '/' . $bottom;
+ }
+
+ // Exposure Time
+ if ($tag == '829a') {
+ if ($bottom != 0) {
+ $data = $top . '/' . $bottom;
+ } else {
+ $data = 0;
+ }
+ }
+
+ } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+ $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) {
+ $data = substr($data, 0, 4);
+ }
+ $data = hexdec($data);
+ if ($type == 'SSHORT' && $data > 32767) {
+ // this makes the number signed instead of unsigned
+ $data = $data - 65536;
+ }
+ if ($type == 'SLONG' && $data > 2147483647) {
+ // this makes the number signed instead of unsigned
+ $data = $data - 4294967296;
+ }
+ } elseif ($type == 'UNDEFINED') {
+ // ExifVersion,FlashPixVersion,InteroperabilityVersion
+ if ($tag == '9000' || $tag == 'a000' || $tag == '0002') {
+ $data = sprintf(_("version %d"), $data / 100);
+ }
+ } else {
+ $data = bin2hex($data);
+ if ($intel == 1) $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ return $data;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ */
+class Horde_Image_Exif_Parser_Base
+{
+ /**
+ *
+ * @param $type
+ * @param $size
+ * @return unknown_type
+ */
+ static protected function _lookupType(&$type,&$size) {
+ switch($type) {
+ case '0001': $type = 'UBYTE'; $size=1; break;
+ case '0002': $type = 'ASCII'; $size=1; break;
+ case '0003': $type = 'USHORT'; $size=2; break;
+ case '0004': $type = 'ULONG'; $size=4; break;
+ case '0005': $type = 'URATIONAL'; $size=8; break;
+ case '0006': $type = 'SBYTE'; $size=1; break;
+ case '0007': $type = 'UNDEFINED'; $size=1; break;
+ case '0008': $type = 'SSHORT'; $size=2; break;
+ case '0009': $type = 'SLONG'; $size=4; break;
+ case '000a': $type = 'SRATIONAL'; $size=8; break;
+ case '000b': $type = 'FLOAT'; $size=4; break;
+ case '000c': $type = 'DOUBLE'; $size=8; break;
+ default: $type = 'error:'.$type; $size=0; break;
+ }
+
+ return $type;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ * @author mrubinsk
+ */
+
+/**
+ * Exifer
+ * Extracts EXIF information from digital photos.
+ *
+ * Copyright 2003 Jake Olefsky
+ * http://www.offsky.com/software/exif/index.php
+ * jake@olefsky.com
+ *
+ * Please see exif.php for the complete information about this software.
+ *
+ * ------------
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+class Horde_Image_Exif_Parser_Canon extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ *Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch($tag) {
+ case '0001': $tag = 'Settings 1'; break;
+ case '0004': $tag = 'Settings 4'; break;
+ case '0006': $tag = 'ImageType'; break;
+ case '0007': $tag = 'FirmwareVersion'; break;
+ case '0008': $tag = 'ImageNumber'; break;
+ case '0009': $tag = 'OwnerName'; break;
+ case '000c': $tag = 'CameraSerialNumber'; break;
+ case '000f': $tag = 'CustomFunctions'; break;
+ default: $tag = sprintf(_("Unknown: (%s)"), $tag); break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ * Formats Data for the data type
+ */
+ static protected function _formatData($type, $tag, $intel, $data, $exif, &$result)
+ {
+ $place = 0;
+
+ if ($type == 'ASCII') {
+ $result = $data = str_replace('\0', '', $data);
+ } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $top = hexdec(substr($data, 8, 8));
+ $bottom = hexdec(substr($data, 0, 8));
+ if ($bottom != 0) {
+ $data = $top / $bottom;
+ } elseif ($top == 0) {
+ $data = 0;
+ } else {
+ $data = $top . '/' . $bottom;
+ }
+
+ if ($tag == '0204') { //DigitalZoom
+ $data = $data . 'x';
+ }
+ } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+ $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+ $data = bin2hex($data);
+ $result['RAWDATA'] = $data;
+
+ if ($tag == '0001') { //first chunk
+ $result['Bytes'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;
+ if ($result['Bytes'] != strlen($data) / 2) {
+ return $result; //Bad chunk
+ }
+ $result['Macro'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//1
+ switch($result['Macro']) {
+ case 1: $result['Macro'] = _("Macro"); break;
+ case 2: $result['Macro'] = _("Normal"); break;
+ default: $result['Macro'] = _("Unknown");
+ }
+ $result['SelfTimer'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//2
+ switch($result['SelfTimer']) {
+ case 0: $result['SelfTimer'] = _("Off"); break;
+ default: $result['SelfTimer'] .= _("/10s");
+ }
+ $result['Quality'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//3
+ switch($result['Quality']) {
+ case 2: $result['Quality'] = _("Normal"); break;
+ case 3: $result['Quality'] = _("Fine"); break;
+ case 5: $result['Quality'] = _("Superfine"); break;
+ default: $result['Quality'] = _("Unknown");
+ }
+ $result['Flash'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//4
+ switch($result['Flash']) {
+ case 0: $result['Flash'] = _("Off"); break;
+ case 1: $result['Flash'] = _("Auto"); break;
+ case 2: $result['Flash'] = _("On"); break;
+ case 3: $result['Flash'] = _("Red Eye Reduction"); break;
+ case 4: $result['Flash'] = _("Slow Synchro"); break;
+ case 5: $result['Flash'] = _("Auto + Red Eye Reduction"); break;
+ case 6: $result['Flash'] = _("On + Red Eye Reduction"); break;
+ case 16: $result['Flash'] = _("External Flash"); break;
+ default: $result['Flash'] = _("Unknown");
+ }
+ $result['DriveMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//5
+ switch($result['DriveMode']) {
+ case 0: $result['DriveMode'] = _("Single/Timer"); break;
+ case 1: $result['DriveMode'] = _("Continuous"); break;
+ default: $result['DriveMode'] = _("Unknown");
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//6
+ $result['FocusMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//7
+ switch($result['FocusMode']) {
+ case 0: $result['FocusMode'] = _("One-Shot"); break;
+ case 1: $result['FocusMode'] = _("AI Servo"); break;
+ case 2: $result['FocusMode'] = _("AI Focus"); break;
+ case 3: $result['FocusMode'] = _("Manual Focus"); break;
+ case 4: $result['FocusMode'] = _("Single"); break;
+ case 5: $result['FocusMode'] = _("Continuous"); break;
+ case 6: $result['FocusMode'] = _("Manual Focus"); break;
+ default: $result['FocusMode'] = _("Unknown");
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//8
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place,4 )));
+ $place+=4;//9
+ $result['ImageSize'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//10
+ switch($result['ImageSize']) {
+ case 0: $result['ImageSize'] = _("Large"); break;
+ case 1: $result['ImageSize'] = _("Medium"); break;
+ case 2: $result['ImageSize'] = _("Small"); break;
+ default: $result['ImageSize'] = _("Unknown");
+ }
+ $result['EasyShooting'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//11
+ switch($result['EasyShooting']) {
+ case 0: $result['EasyShooting'] = _("Full Auto"); break;
+ case 1: $result['EasyShooting'] = _("Manual"); break;
+ case 2: $result['EasyShooting'] = _("Landscape"); break;
+ case 3: $result['EasyShooting'] = _("Fast Shutter"); break;
+ case 4: $result['EasyShooting'] = _("Slow Shutter"); break;
+ case 5: $result['EasyShooting'] = _("Night"); break;
+ case 6: $result['EasyShooting'] = _("Black & White"); break;
+ case 7: $result['EasyShooting'] = _("Sepia"); break;
+ case 8: $result['EasyShooting'] = _("Portrait"); break;
+ case 9: $result['EasyShooting'] = _("Sport"); break;
+ case 10: $result['EasyShooting'] = _("Macro/Close-Up"); break;
+ case 11: $result['EasyShooting'] = _("Pan Focus"); break;
+ default: $result['EasyShooting'] = _("Unknown");
+ }
+ $result['DigitalZoom'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//12
+ switch($result['DigitalZoom']) {
+ case 0:
+ case 65535: $result['DigitalZoom'] = _("None"); break;
+ case 1: $result['DigitalZoom'] = _("2x"); break;
+ case 2: $result['DigitalZoom'] = _("4x"); break;
+ default: $result['DigitalZoom'] = _("Unknown");
+ }
+ $result['Contrast'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//13
+ switch($result['Contrast']) {
+ case 0: $result['Contrast'] = _("Normal"); break;
+ case 1: $result['Contrast'] = _("High"); break;
+ case 65535: $result['Contrast'] = _("Low"); break;
+ default: $result['Contrast'] = _("Unknown");
+ }
+ $result['Saturation'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//14
+ switch($result['Saturation']) {
+ case 0: $result['Saturation'] = _("Normal"); break;
+ case 1: $result['Saturation'] = _("High"); break;
+ case 65535: $result['Saturation'] = _("Low"); break;
+ default: $result['Saturation'] = _("Unknown");
+ }
+ $result['Sharpness'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//15
+ switch($result['Sharpness']) {
+ case 0: $result['Sharpness'] = _("Normal"); break;
+ case 1: $result['Sharpness'] = _("High"); break;
+ case 65535: $result['Sharpness'] = _("Low"); break;
+ default: $result['Sharpness'] = _("Unknown");
+ }
+ $result['ISO'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//16
+ switch($result['ISO']) {
+ case 32767:
+ case 0:
+ $result['ISO'] = isset($exif['SubIFD']['ISOSpeedRatings']) ?
+ $exif['SubIFD']['ISOSpeedRatings'] :
+ 'Unknown';
+ break;
+ case 15:
+ $result['ISO'] = _("Auto");
+ break;
+ case 16:
+ $result['ISO'] = 50;
+ break;
+ case 17:
+ $result['ISO'] = 100;
+ break;
+ case 18:
+ $result['ISO'] = 200;
+ break;
+ case 19:
+ $result['ISO'] = 400;
+ break;
+ default:
+ $result['ISO'] = _("Unknown");
+ }
+ $result['MeteringMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//17
+ switch($result['MeteringMode']) {
+ case 3: $result['MeteringMode'] = _("Evaluative"); break;
+ case 4: $result['MeteringMode'] = _("Partial"); break;
+ case 5: $result['MeteringMode'] = _("Center-weighted"); break;
+ default: $result['MeteringMode'] = _("Unknown");
+ }
+ $result['FocusType'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//18
+ switch($result['FocusType']) {
+ case 0: $result['FocusType'] = _("Manual"); break;
+ case 1: $result['FocusType'] = _("Auto"); break;
+ case 3: $result['FocusType'] = _("Close-up (Macro)"); break;
+ case 8: $result['FocusType'] = _("Locked (Pan Mode)"); break;
+ default: $result['FocusType'] = _("Unknown");
+ }
+ $result['AFPointSelected'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//19
+ switch($result['AFPointSelected']) {
+ case 12288: $result['AFPointSelected'] = _("Manual Focus"); break;
+ case 12289: $result['AFPointSelected'] = _("Auto Selected"); break;
+ case 12290: $result['AFPointSelected'] = _("Right"); break;
+ case 12291: $result['AFPointSelected'] = _("Center"); break;
+ case 12292: $result['AFPointSelected'] = _("Left"); break;
+ default: $result['AFPointSelected'] = _("Unknown");
+ }
+ $result['ExposureMode'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//20
+ switch($result['ExposureMode']) {
+ case 0: $result['ExposureMode'] = _("EasyShoot"); break;
+ case 1: $result['ExposureMode'] = _("Program"); break;
+ case 2: $result['ExposureMode'] = _("Tv"); break;
+ case 3: $result['ExposureMode'] = _("Av"); break;
+ case 4: $result['ExposureMode'] = _("Manual"); break;
+ case 5: $result['ExposureMode'] = _("Auto-DEP"); break;
+ default: $result['ExposureMode'] = _("Unknown");
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//21
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//22
+ $result['LongFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//23
+ $result['LongFocalLength'] .= 'focal units';
+ $result['ShortFocalLength'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//24
+ $result['ShortFocalLength'] .= ' focal units';
+ $result['FocalUnits'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//25
+ $result['FocalUnits'] .= ' per mm';
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//26
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//27
+ $result['FlashActivity'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//28
+ switch($result['FlashActivity']) {
+ case 0: $result['FlashActivity'] = _("Flash Did Not Fire"); break;
+ case 1: $result['FlashActivity'] = _("Flash Fired"); break;
+ default: $result['FlashActivity'] = _("Unknown");
+ }
+ $result['FlashDetails'] = str_pad(base_convert(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)), 16, 2), 16, '0', STR_PAD_LEFT);
+ $place += 4;//29
+ $flashDetails = array();
+ if (substr($result['FlashDetails'], 1, 1) == 1) {
+ $flashDetails[] = _("External E-TTL");
+ }
+ if (substr($result['FlashDetails'], 2, 1) == 1) {
+ $flashDetails[] = _("Internal Flash");
+ }
+ if (substr($result['FlashDetails'], 4, 1) == 1) {
+ $flashDetails[] = _("FP sync used");
+ }
+ if (substr($result['FlashDetails'], 8, 1) == 1) {
+ $flashDetails[] = _("2nd(rear)-curtain sync used");
+ }
+ if (substr($result['FlashDetails'], 12, 1) == 1) {
+ $flashDetails[] = _("1st curtain sync");
+ }
+ $result['FlashDetails'] = implode(',', $flashDetails);
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//30
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//31
+ $anotherFocusMode = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//32
+ if (strpos(strtoupper($exif['IFD0']['Model']), 'G1') !== false) {
+ switch($anotherFocusMode) {
+ case 0: $result['FocusMode'] = _("Single"); break;
+ case 1: $result['FocusMode'] = _("Continuous"); break;
+ default: $result['FocusMode'] = _("Unknown");
+ }
+ }
+
+ } elseif ($tag=='0004') { //second chunk
+ $result['Bytes']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//0
+ if ($result['Bytes'] != strlen($data) / 2) {
+ return $result; //Bad chunk
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//1
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//2
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//3
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//4
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//5
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//6
+ $result['WhiteBalance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//7
+ switch($result['WhiteBalance']) {
+ case 0: $result['WhiteBalance'] = _("Auto"); break;
+ case 1: $result['WhiteBalance'] = _("Sunny"); break;
+ case 2: $result['WhiteBalance'] = _("Cloudy"); break;
+ case 3: $result['WhiteBalance'] = _("Tungsten"); break;
+ case 4: $result['WhiteBalance'] = _("Fluorescent"); break;
+ case 5: $result['WhiteBalance'] = _("Flash"); break;
+ case 6: $result['WhiteBalance'] = _("Custom"); break;
+ default: $result['WhiteBalance'] = _("Unknown");
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//8
+ $result['SequenceNumber'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//9
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//10
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//11
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data ,$place, 4)));
+ $place += 4;//12
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//13
+ $result['AFPointUsed']=hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//14
+ $afPointUsed = array();
+ if ($result['AFPointUsed'] & 0x0001) {
+ $afPointUsed[] = _("Right"); //bit 0
+ }
+ if ($result['AFPointUsed'] & 0x0002) {
+ $afPointUsed[] = _("Center"); //bit 1
+ }
+ if ($result['AFPointUsed'] & 0x0004) {
+ $afPointUsed[] = _("Left"); //bit 2
+ }
+ if ($result['AFPointUsed'] & 0x0800) {
+ $afPointUsed[] = 12; //bit 12
+ }
+ if ($result['AFPointUsed'] & 0x1000) {
+ $afPointUsed[] = 13; //bit 13
+ }
+ if ($result['AFPointUsed'] & 0x2000) {
+ $afPointUsed[] = 14; //bit 14
+ }
+ if ($result['AFPointUsed'] & 0x4000) {
+ $afPointUsed[] = 15; //bit 15
+ }
+ $result['AFPointUsed'] = implode(',', $afPointUsed);
+ $result['FlashBias'] = Horde_Image_Exif::intel2Moto(substr($data, $place, 4));
+ $place += 4;//15
+ switch($result['FlashBias']) {
+ case 'ffc0': $result['FlashBias'] = '-2 EV'; break;
+ case 'ffcc': $result['FlashBias'] = '-1.67 EV'; break;
+ case 'ffd0': $result['FlashBias'] = '-1.5 EV'; break;
+ case 'ffd4': $result['FlashBias'] = '-1.33 EV'; break;
+ case 'ffe0': $result['FlashBias'] = '-1 EV'; break;
+ case 'ffec': $result['FlashBias'] = '-0.67 EV'; break;
+ case 'fff0': $result['FlashBias'] = '-0.5 EV'; break;
+ case 'fff4': $result['FlashBias'] = '-0.33 EV'; break;
+ case '0000': $result['FlashBias'] = '0 EV'; break;
+ case '000c': $result['FlashBias'] = '0.33 EV'; break;
+ case '0010': $result['FlashBias'] = '0.5 EV'; break;
+ case '0014': $result['FlashBias'] = '0.67 EV'; break;
+ case '0020': $result['FlashBias'] = '1 EV'; break;
+ case '002c': $result['FlashBias'] = '1.33 EV'; break;
+ case '0030': $result['FlashBias'] = '1.5 EV'; break;
+ case '0034': $result['FlashBias'] = '1.67 EV'; break;
+ case '0040': $result['FlashBias'] = '2 EV'; break;
+ default: $result['FlashBias'] = _("Unknown");
+ }
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//16
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//17
+ $result['Unknown'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//18
+ $result['SubjectDistance'] = hexdec(Horde_Image_Exif::intel2Moto(substr($data, $place, 4)));
+ $place += 4;//19
+ $result['SubjectDistance'] .= '/100 m';
+
+ } elseif ($tag=='0008') { //image number
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $data = hexdec($data);
+ $result = round($data / 10000) . '-' . $data % 10000;
+ } elseif ($tag == '000c') { //camera serial number
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $data = hexdec($data);
+ $result = '#' . bin2hex(substr($data, 0, 16)) . substr($data, 16, 16);
+ }
+
+ } elseif ($type != 'UNDEFINED') {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Cannon Special data section
+ * Useful: http://www.burren.cx/david/canon.html
+ * http://www.burren.cx/david/canon.html
+ * http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html
+ */
+ static public function parse($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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ }
+ $tag_name = self::_lookupTag($tag);
+
+ //2 byte type
+ $type = bin2hex(substr($block, $place, 2));
+ $place += 2;
+ if ($intel == 1) {
+ $type = Horde_Image_Exif::intel2Moto($type);
+ }
+ self::_lookupType($type, $size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block, $place, 4));
+ $place += 4;
+ if ($intel == 1) {
+ $count = Horde_Image_Exif::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 = Horde_Image_Exif::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);
+ } elseif ($v == -1) {
+ $result['Errors'] = $result['Errors']++;
+ $data = '';
+ } else {
+ $data = '';
+ }
+ }
+ $result['SubIFD']['MakerNote'][$tag_name] = ''; // insure the index exists
+ $formated_data = self::_formatData($type, $tag, $intel, $data, $result, $result['SubIFD']['MakerNote'][$tag_name]);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+
+ }
+
+}
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/**
+ * Exifer
+ * Extracts EXIF information from digital photos.
+ *
+ * Copyright � 2003 Jake Olefsky
+ * http://www.offsky.com/software/exif/index.php
+ * jake@olefsky.com
+ *
+ * Please see exif.php for the complete information about this software.
+ *
+ * ------------
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+class Horde_Image_Exif_Parser_Fujifilm extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ * Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+ static protected function _lookupTag($tag) {
+
+ switch($tag) {
+ case "0000": $tag = "Version";break;
+ case "1000": $tag = "Quality";break;
+ case "1001": $tag = "Sharpness";break;
+ case "1002": $tag = "WhiteBalance";break;
+ case "1003": $tag = "Color";break;
+ case "1004": $tag = "Tone";break;
+ case "1010": $tag = "FlashMode";break;
+ case "1011": $tag = "FlashStrength";break;
+ case "1020": $tag = "Macro";break;
+ case "1021": $tag = "FocusMode";break;
+ case "1030": $tag = "SlowSync";break;
+ case "1031": $tag = "PictureMode";break;
+ case "1032": $tag = "Unknown";break;
+ case "1100": $tag = "ContinuousTakingBracket";break;
+ case "1200": $tag = "Unknown";break;
+ case "1300": $tag = "BlurWarning";break;
+ case "1301": $tag = "FocusWarning";break;
+ case "1302": $tag = "AEWarning";break;
+
+ default: $tag = "unknown:".$tag;break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ *
+ * @param $type
+ * @param $tag
+ * @param $intel
+ * @param $data
+ * @return unknown_type
+ */
+ static protected function _formatData($type, $tag, $intel, $data)
+ {
+ if($type=="ASCII") {
+
+
+ } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $top = hexdec(substr($data,8,8));
+ $bottom = hexdec(substr($data,0,8));
+ if($bottom!=0) $data=$top/$bottom;
+ else if($top==0) $data = 0;
+ else $data=$top."/".$bottom;
+
+ if($tag=="1011") { //FlashStrength
+ $data=$data." EV";
+ }
+
+ } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $data=hexdec($data);
+
+ if($tag=="1001") { //Sharpness
+ if($data == 1) $data = _("Soft");
+ else if($data == 2) $data = _("Soft");
+ else if($data == 3) $data = _("Normal");
+ else if($data == 4) $data = _("Hard");
+ else if($data == 5) $data = _("Hard");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="1002") { //WhiteBalance
+ if($data == 0) $data = _("Auto");
+ else if($data == 256) $data = _("Daylight");
+ else if($data == 512) $data = _("Cloudy");
+ else if($data == 768) $data = _("DaylightColor-fluorescence");
+ else if($data == 769) $data = _("DaywhiteColor-fluorescence");
+ else if($data == 770) $data = _("White-fluorescence");
+ else if($data == 1024) $data = _("Incandescense");
+ else if($data == 3840) $data = _("Custom");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="1003") { //Color
+ if($data == 0) $data = _("Chroma Saturation Normal(STD)");
+ else if($data == 256) $data = _("Chroma Saturation High");
+ else if($data == 512) $data = _("Chroma Saturation Low(ORG)");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1004") { //Tone
+ if($data == 0) $data = _("Contrast Normal(STD)");
+ else if($data == 256) $data = _("Contrast High(HARD)");
+ else if($data == 512) $data = _("Contrast Low(ORG)");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1010") { //FlashMode
+ if($data == 0) $data = _("Auto");
+ else if($data == 1) $data = _("On");
+ else if($data == 2) $data = _("Off");
+ else if($data == 3) $data = _("Red-Eye Reduction");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1020") { //Macro
+ if($data == 0) $data = _("Off");
+ else if($data == 1) $data = _("On");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1021") { //FocusMode
+ if($data == 0) $data = _("Auto");
+ else if($data == 1) $data = _("Manual");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1030") { //SlowSync
+ if($data == 0) $data = _("Off");
+ else if($data == 1) $data = _("On");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1031") { //PictureMode
+ if($data == 0) $data = _("Auto");
+ else if($data == 1) $data = _("Portrait");
+ else if($data == 2) $data = _("Landscape");
+ else if($data == 4) $data = _("Sports");
+ else if($data == 5) $data = _("Night");
+ else if($data == 6) $data = _("Program AE");
+ else if($data == 256) $data = _("Aperture Prority AE");
+ else if($data == 512) $data = _("Shutter Priority");
+ else if($data == 768) $data = _("Manual Exposure");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1100") { //ContinuousTakingBracket
+ if($data == 0) $data = _("Off");
+ else if($data == 1) $data = _("On");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1300") { //BlurWarning
+ if($data == 0) $data = _("No Warning");
+ else if($data == 1) $data = _("Warning");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1301") { //FocusWarning
+ if($data == 0) $data = _("Auto Focus Good");
+ else if($data == 1) $data = _("Out of Focus");
+ else $data = _("Unknown: ").$data;
+ }
+ if($tag=="1302") { //AEWarning
+ if($data == 0) $data = _("AE Good");
+ else if($data == 1) $data = _("Over Exposure");
+ else $data = _("Unknown: ").$data;
+ }
+ } else if($type=="UNDEFINED") {
+
+
+
+ } else {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ return $data;
+ }
+
+ /**
+ *
+ * @param $block
+ * @param $result
+ * @return unknown_type
+ */
+ static public function parse($block, &$result)
+ {
+ $intel=1;
+
+ $model = $result['IFD0']['Model'];
+
+ $place=8; //current place
+ $offset=8;
+
+
+ $num = bin2hex(substr($block,$place,4));$place+=4;
+ if($intel==1) $num = Horde_Image_Exif::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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag);
+
+ //2 byte type
+ $type = bin2hex(substr($block,$place,2));$place+=2;
+ if($intel==1) $type = Horde_Image_Exif::intel2Moto($type);
+ self::_lookupType($type,$size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block,$place,4));$place+=4;
+ if($intel==1) $count = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($value);
+ $data = substr($block,hexdec($value)-$offset,$bytesofdata*2);
+ }
+ $formated_data = self::_formatData($type,$tag,$intel,$data);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/**
+ Exifer
+ Extracts EXIF information from digital photos.
+
+ Copyright © 2003 Jake Olefsky
+ http://www.offsky.com/software/exif/index.php
+ jake@olefsky.com
+
+ Please see exif.php for the complete information about this software.
+
+ ------------
+
+ This program is free software; you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+class Horde_Image_Exif_Parser_Gps extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ * Looks up the name of the tag
+ *
+ * @param unknown_type $tag
+ * @return string
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch($tag) {
+ case "0000": return "Version";
+ case "0001": return "LatitudeRef"; //north or south
+ case "0002": return "Latitude"; //dd mm.mm or dd mm ss
+ case "0003": return "LongitudeRef"; //east or west
+ case "0004": return "Longitude"; //dd mm.mm or dd mm ss
+ case "0005": return "AltitudeRef"; //sea level or below sea level
+ case "0006": return "Altitude"; //positive rational number
+ case "0007": return "Time"; //three positive rational numbers
+ case "0008": return "Satellite"; //text string up to 999 bytes long
+ case "0009": return "ReceiveStatus"; //in progress or interop
+ case "000a": return "MeasurementMode"; //2D or 3D
+ case "000b": return "MeasurementPrecision"; //positive rational number
+ case "000c": return "SpeedUnit"; //KPH, MPH, knots
+ case "000d": return "ReceiverSpeed"; //positive rational number
+ case "000e": return "MovementDirectionRef"; //true or magnetic north
+ case "000f": return "MovementDirection"; //positive rational number
+ case "0010": return "ImageDirectionRef"; //true or magnetic north
+ case "0011": return "ImageDirection"; //positive rational number
+ case "0012": return "GeodeticSurveyData"; //text string up to 999 bytes long
+ case "0013": return "DestLatitudeRef"; //north or south
+ case "0014": return "DestinationLatitude"; //three positive rational numbers
+ case "0015": return "DestLongitudeRef"; //east or west
+ case "0016": return "DestinationLongitude"; //three positive rational numbers
+ case "0017": return "DestBearingRef"; //true or magnetic north
+ case "0018": return "DestinationBearing"; //positive rational number
+ case "0019": return "DestDistanceRef"; //km, miles, knots
+ case "001a": return "DestinationDistance"; //positive rational number
+ case "001b": return "ProcessingMethod";
+ case "001c": return "AreaInformation";
+ case "001d": return "Datestamp"; //text string 10 bytes long
+ case "001e": return "DifferentialCorrection"; //integer in range 0-65535
+ default: return "unknown:".$tag;
+ }
+
+ }
+
+ /**
+ * Formats a rational number
+ */
+ static protected function _rational($data, $intel)
+ {
+
+ if ($intel == 1) {
+ $top = hexdec(substr($data, 8, 8)); //intel stores them bottom-top
+ } else {
+ $top = hexdec(substr($data, 0, 8)); //motorola stores them top-bottom
+ }
+
+ if ($intel == 1) {
+ $bottom = hexdec(substr($data, 0, 8));
+ } else {
+ $bottom = hexdec(substr($data, 8, 8));
+ }
+
+ if ($bottom!=0) {
+ $data = $top / $bottom;
+ } elseif ($top == 0) {
+ $data = 0;
+ } else {
+ $data = $top . "/" . $bottom;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Formats Data for the data type
+ */
+ static protected function _formatData($type, $tag, $intel, $data)
+ {
+
+ if($type == "ASCII") {
+ // Latitude Reference, Longitude Reference
+ if ($tag == "0001" || $tag == "0003") {
+ $data = ($data{1} == $data{2} && $data{1} == $data{3}) ? $data{0} : $data;
+ }
+ } elseif ($type == "URATIONAL" || $type == "SRATIONAL") {
+ $data = bin2hex($data);
+ if ($intel ==1 ) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ if ($intel == 1) {
+ $top = hexdec(substr($data, 8, 8)); //intel stores them bottom-top
+ } else {
+ $top = hexdec(substr($data, 0, 8)); //motorola stores them top-bottom
+ }
+
+ if ($intel == 1) {
+ $bottom = hexdec(substr($data, 0, 8));
+ } else {
+ $bottom = hexdec(substr($data, 8, 8));
+ }
+
+ if ($type == "SRATIONAL" && $top > 2147483647) {
+ // make the number signed instead of unsigned
+ $top = $top - 4294967296;
+ }
+
+ //Latitude, Longitude
+ if ($tag=="0002" || $tag=="0004") {
+ if ($intel == 1) {
+ $seconds = self::_rational(substr($data, 0, 16), $intel);
+ $hour = self::_rational(substr($data, 32, 16), $intel);
+ } else {
+ $hour = self::_rational(substr($data, 0, 16), $intel);
+ $seconds = self::_rational(substr($data, 32, 16), $intel);
+ }
+ $minutes = self::_rational(substr($data, 16, 16), $intel);
+ $data = array($hour, $minutes, $seconds);
+ } elseif ($tag == "0007") { //Time
+ $seconds = self::_rational(substr($data, 0, 16), $intel);
+ $minutes = self::_rational(substr($data, 16, 16), $intel);
+ $hour = self::_rational(substr($data, 32, 16), $intel);
+ $data = $hour . ":" . $minutes . ":" . $seconds;
+ } else {
+ if ($bottom != 0) {
+ $data = $top / $bottom;
+ } elseif ($top == 0) {
+ $data = 0;
+ } else {
+ $data = $top . "/" . $bottom;
+ }
+ if ($tag == "0006") {
+ $data .= 'm';
+ }
+ }
+ } elseif ($type == "USHORT" || $type == "SSHORT" || $type == "ULONG" ||
+ $type == "SLONG" || $type == "FLOAT" || $type == "DOUBLE") {
+
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $data = hexdec($data);
+ } elseif ($type == "UNDEFINED") {
+ } elseif ($type == "UBYTE") {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $num = Horde_Image_Exif::intel2Moto($data);
+ }
+ if ($tag == "0000") { // VersionID
+ $data = hexdec(substr($data, 0, 2))
+ . '.' . hexdec(substr($data, 2, 2))
+ . '.' . hexdec(substr($data, 4, 2))
+ . '.'. hexdec(substr($data, 6, 2));
+ } elseif ($tag == "0005") { // Altitude Reference
+ if ($data == "00000000") {
+ $data = 'Above Sea Level';
+ } elseif ($data == "01000000") {
+ $data = 'Below Sea Level';
+ }
+ }
+ } else {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * GPS Special data section
+ * Useful websites
+ * http://drewnoakes.com/code/exif/sampleOutput.html
+ * http://www.geosnapper.com
+ */
+ static public function parse($block,&$result,$offset,$seek, $globalOffset)
+ {
+ if ($result['Endien'] == "Intel") {
+ $intel = 1;
+ } else {
+ $intel = 0;
+ }
+
+ //offsets are from TIFF header which is 12 bytes from the start of the file
+ $v = fseek($seek, $globalOffset + $offset);
+ if ($v == -1) {
+ $result['Errors'] = $result['Errors']++;
+ }
+
+ $num = bin2hex(fread($seek, 2));
+ if ($intel == 1) {
+ $num = Horde_Image_Exif::intel2Moto($num);
+ }
+ $num = hexdec($num);
+ $result['GPS']['NumTags'] = $num;
+ $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 = Horde_Image_Exif::intel2Moto($tag);
+ }
+ $tag_name = self::_lookpTag($tag);
+
+ //2 byte datatype
+ $type = bin2hex(substr($block, $place, 2));
+ $place += 2;
+ if ($intel == 1) {
+ $type = Horde_Image_Exif::intel2Moto($type);
+ }
+ self::_lookupType($type, $size);
+
+ //4 byte number of elements
+ $count = bin2hex(substr($block, $place, 4));
+ $place += 4;
+ if ($intel==1) {
+ $count = Horde_Image_Exif::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 {
+ $value = bin2hex($value);
+ if ($intel == 1) {
+ $value = Horde_Image_Exif::intel2Moto($value);
+ }
+ //offsets are from TIFF header which is 12 bytes from the start of the file
+ $v = fseek($seek, $globalOffset + hexdec($value));
+ if ($v == 0) {
+ $data = fread($seek, $bytesofdata);
+ } elseif ($v == -1) {
+ $result['Errors'] = $result['Errors']++;
+ }
+ }
+ $result['GPS' . $tag_name] = self::_formatData($type, $tag, $intel, $data);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/*
+ Exifer
+ Extracts EXIF information from digital photos.
+
+ Copyright � 2003 Jake Olefsky
+ http://www.offsky.com/software/exif/index.php
+ jake@olefsky.com
+
+ Please see exif.php for the complete information about this software.
+
+ ------------
+
+ This program is free software; you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+class Horde_Image_Exif_Parser_Nikon extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ *
+ * @param $tag
+ * @param $model
+ * @return unknown_type
+ */
+ static protected function _lookupTag($tag,$model)
+ {
+ if($model==0) {
+ switch($tag) {
+ case "0003": $tag = "Quality";break;
+ case "0004": $tag = "ColorMode";break;
+ case "0005": $tag = "ImageAdjustment";break;
+ case "0006": $tag = "CCDSensitivity";break;
+ case "0007": $tag = "WhiteBalance";break;
+ case "0008": $tag = "Focus";break;
+ case "0009": $tag = "Unknown2";break;
+ case "000a": $tag = "DigitalZoom";break;
+ case "000b": $tag = _("Converter");break;
+
+ default: $tag = "unknown:".$tag;break;
+ }
+ } else if($model==1) {
+ switch($tag) {
+ case "0002": $tag = "ISOSetting";break;
+ case "0003": $tag = "ColorMode";break;
+ case "0004": $tag = "Quality";break;
+ case "0005": $tag = "Whitebalance";break;
+ case "0006": $tag = "ImageSharpening";break;
+ case "0007": $tag = "FocusMode";break;
+ case "0008": $tag = "FlashSetting";break;
+ case "0009": $tag = "FlashMode";break;
+ case "000b": $tag = "WhiteBalanceFine";break;
+ case "000f": $tag = "ISOSelection";break;
+ case "0013": $tag = "ISOSelection2";break;
+ case "0080": $tag = "ImageAdjustment";break;
+ case "0081": $tag = "ToneCompensation";break;
+ case "0082": $tag = "Adapter";break;
+ case "0083": $tag = "LensType";break;
+ case "0084": $tag = "LensInfo";break;
+ case "0085": $tag = "ManualFocusDistance";break;
+ case "0086": $tag = "DigitalZoom";break;
+ case "0087": $tag = "FlashUsed";break;
+ case "0088": $tag = "AFFocusPosition";break;
+ case "008d": $tag = "ColorMode";break;
+ case "0090": $tag = "LightType";break;
+ case "0094": $tag = "Saturation";break;
+ case "0095": $tag = "NoiseReduction";break;
+ case "0010": $tag = "DataDump";break;
+
+ default: $tag = "unknown:".$tag;break;
+ }
+ }
+
+ return $tag;
+ }
+
+ /**
+ *
+ * @param $type
+ * @param $tag
+ * @param $intel
+ * @param $model
+ * @param $data
+ * @return unknown_type
+ */
+ static protected function _formatData($type,$tag,$intel,$model,$data)
+ {
+ if($type=="ASCII") {
+
+
+ } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $top = hexdec(substr($data,8,8));
+ $bottom = hexdec(substr($data,0,8));
+ if($bottom!=0) $data=$top/$bottom;
+ else if($top==0) $data = 0;
+ else $data=$top."/".$bottom;
+
+ if($tag=="0085" && $model==1) { //ManualFocusDistance
+ $data=$data." m";
+ }
+ if($tag=="0086" && $model==1) { //DigitalZoom
+ $data=$data."x";
+ }
+ if($tag=="000a" && $model==0) { //DigitalZoom
+ $data=$data."x";
+ }
+ } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $data=hexdec($data);
+
+ if($tag=="0003" && $model==0) { //Quality
+ if($data == 1) $data = _("VGA Basic");
+ else if($data == 2) $data = _("VGA Normal");
+ else if($data == 3) $data = _("VGA Fine");
+ else if($data == 4) $data = _("SXGA Basic");
+ else if($data == 5) $data = _("SXGA Normal");
+ else if($data == 6) $data = _("SXGA Fine");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0004" && $model==0) { //Color
+ if($data == 1) $data = _("Color");
+ else if($data == 2) $data = _("Monochrome");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0005" && $model==0) { //Image Adjustment
+ if($data == 0) $data = _("Normal");
+ else if($data == 1) $data = _("Bright+");
+ else if($data == 2) $data = _("Bright-");
+ else if($data == 3) $data = _("Contrast+");
+ else if($data == 4) $data = _("Contrast-");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0006" && $model==0) { //CCD Sensitivity
+ if($data == 0) $data = "ISO-80";
+ else if($data == 2) $data = "ISO-160";
+ else if($data == 4) $data = "ISO-320";
+ else if($data == 5) $data = "ISO-100";
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0007" && $model==0) { //White Balance
+ if($data == 0) $data = _("Auto");
+ else if($data == 1) $data = _("Preset");
+ else if($data == 2) $data = _("Daylight");
+ else if($data == 3) $data = _("Incandescense");
+ else if($data == 4) $data = _("Flourescence");
+ else if($data == 5) $data = _("Cloudy");
+ else if($data == 6) $data = _("SpeedLight");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="000b" && $model==0) { //Converter
+ if($data == 0) $data = _("None");
+ else if($data == 1) $data = _("Fisheye");
+ else $data = _("Unknown").": ".$data;
+ }
+ } else if($type=="UNDEFINED") {
+
+ if($tag=="0001" && $model==1) { //Unknown (Version?)
+ $data=$data/100;
+ }
+ if($tag=="0088" && $model==1) { //AF Focus Position
+ $temp = _("Center");
+ $data = bin2hex($data);
+ $data = str_replace("01","Top",$data);
+ $data = str_replace("02","Bottom",$data);
+ $data = str_replace("03","Left",$data);
+ $data = str_replace("04","Right",$data);
+ $data = str_replace("00","",$data);
+ if(strlen($data)==0) $data = $temp;
+ }
+
+ } else {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+
+ if($tag=="0083" && $model==1) { //Lens Type
+ $data = hexdec(substr($data,0,2));
+ if($data == 0) $data = _("AF non D");
+ else if($data == 1) $data = _("Manual");
+ else if($data == 2) $data = "AF-D or AF-S";
+ else if($data == 6) $data = "AF-D G";
+ else if($data == 10) $data = "AF-D VR";
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0087" && $model==1) { //Flash type
+ $data = hexdec(substr($data,0,2));
+ if($data == 0) $data = _("Did Not Fire");
+ else if($data == 4) $data = _("Unknown");
+ else if($data == 7) $data = _("External");
+ else if($data == 9) $data = _("On Camera");
+ else $data = _("Unknown").": ".$data;
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ *
+ * @param $block
+ * @param $result
+ * @return unknown_type
+ */
+ static public function parse($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\0" || $model=="E800\0" || $model=="E900\0" || $model=="E900S\0" || $model=="E910\0" || $model=="E950\0") {
+ $place=8; //current place
+ $model = 0;
+
+ //Get number of tags (2 bytes)
+ $num = bin2hex(substr($block,$place,2));$place+=2;
+ if($intel==1) $num = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag, $model);
+
+ //2 byte type
+ $type = bin2hex(substr($block,$place,2));$place+=2;
+ if($intel==1) $type = Horde_Image_Exif::intel2Moto($type);
+ self::_lookupType($type,$size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block,$place,4));$place+=4;
+ if($intel==1) $count = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($value);
+ $data = substr($block,hexdec($value)-$offset,$bytesofdata*2);
+ }
+ $formated_data = self::_formatData($type,$tag,$intel,$model,$data);
+ $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 = Horde_Image_Exif::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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag, $model);
+
+ //2 byte type
+ $type = bin2hex(substr($block,$place,2));$place+=2;
+ if($intel==1) $type = Horde_Image_Exif::intel2Moto($type);
+ self::_lookupType($type,$size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block,$place,4));$place+=4;
+ if($intel==1) $count = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($value);
+ $data = substr($block,hexdec($value)+hexdec($offset)+2,$bytesofdata);
+ }
+ $formated_data = self::_formatData($type,$tag,$intel,$model,$data);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/*
+ Exifer
+ Extracts EXIF information from digital photos.
+
+ Copyright � 2003 Jake Olefsky
+ http://www.offsky.com/software/exif/index.php
+ jake@olefsky.com
+
+ Please see exif.php for the complete information about this software.
+
+ ------------
+
+ This program is free software; you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+class Horde_Image_Exif_Parser_Olympus extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ *
+ * @param $tag
+ * @return unknown_type
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch($tag) {
+ case "0200": $tag = "SpecialMode";break;
+ case "0201": $tag = "JpegQual";break;
+ case "0202": $tag = "Macro";break;
+ case "0203": $tag = "Unknown1";break;
+ case "0204": $tag = "DigiZoom";break;
+ case "0205": $tag = "Unknown2";break;
+ case "0206": $tag = "Unknown3";break;
+ case "0207": $tag = "SoftwareRelease";break;
+ case "0208": $tag = "PictInfo";break;
+ case "0209": $tag = "CameraID";break;
+ case "0f00": $tag = "DataDump";break;
+
+ default: $tag = "unknown:".$tag;break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ *
+ * @param $type
+ * @param $tag
+ * @param $intel
+ * @param $data
+ * @return unknown_type
+ */
+ static protected function _formatData($type,$tag,$intel,$data)
+ {
+ if($type=="ASCII") {
+
+ } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $top = hexdec(substr($data,8,8));
+ $bottom = hexdec(substr($data,0,8));
+ if($bottom!=0) $data=$top/$bottom;
+ else if($top==0) $data = 0;
+ else $data=$top."/".$bottom;
+
+ if($tag=="0204") { //DigitalZoom
+ $data=$data."x";
+ }
+ if($tag=="0205") { //Unknown2
+ $data=$top."/".$bottom;
+ }
+ } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $data=hexdec($data);
+
+ if($tag=="0201") { //JPEGQuality
+ if($data == 1) $data = "SQ";
+ else if($data == 2) $data = "HQ";
+ else if($data == 3) $data = "SHQ";
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0202") { //Macro
+ if($data == 0) $data = "Normal";
+ else if($data == 1) $data = "Macro";
+ else $data = _("Unknown").": ".$data;
+ }
+ } else if($type=="UNDEFINED") {
+
+ } else {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ return $data;
+ }
+
+ /**
+ *
+ * @param $block
+ * @param $result
+ * @param $seek
+ * @param $globalOffset
+ * @return unknown_type
+ */
+ static public function parse($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\x00") {
+ $new = true;
+ } else if (substr($block, 0, 7) == "OLYMP\x00\x01"
+ || substr($block, 0, 7) == "OLYMP\x00\x02") {
+ $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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag);
+
+ //2 byte type
+ $type = bin2hex(substr($block, $place,2));
+ $place += 2;
+ if ($intel == 1) $type = Horde_Image_Exif::intel2Moto($type);
+ self::_lookupType($type,$size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block, $place,4));
+ $place+=4;
+ if ($intel == 1) $count = Horde_Image_Exif::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 = Horde_Image_Exif::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 = self::_formatData($type,$tag,$intel,$data);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/**
+ * Exifer
+ * Extracts EXIF information from digital photos.
+ *
+ * Copyright 2003 Jake Olefsky
+ * http://www.offsky.com/software/exif/index.php
+ * jake@olefsky.com
+ *
+ * Please see exif.php for the complete information about this software.
+ *
+ * ------------
+ *
+ * This program is free software; you can redistribute it and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+ */
+class Horde_Image_Exif_Parser_Panasonic extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ * Looks up the name of the tag for the MakerNote (Depends on Manufacturer)
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch ($tag) {
+ case '0001': $tag = 'Quality'; break;
+ case '0002': $tag = 'FirmwareVersion'; break;
+ case '0003': $tag = 'WhiteBalance'; break;
+ case '0007': $tag = 'FocusMode'; break;
+ case '000f': $tag = 'AFMode'; break;
+ case '001a': $tag = 'ImageStabilizer'; break;
+ case '001c': $tag = 'MacroMode'; break;
+ case '001f': $tag = 'ShootingMode'; break;
+ case '0020': $tag = 'Audio'; break;
+ case '0021': $tag = 'DataDump'; break;
+ case '0023': $tag = 'WhiteBalanceBias'; break;
+ case '0024': $tag = 'FlashBias'; break;
+ case '0025': $tag = 'SerialNumber'; break;
+ case '0028': $tag = 'ColourEffect'; break;
+ case '002a': $tag = 'BurstMode'; break;
+ case '002b': $tag = 'SequenceNumber'; break;
+ case '002c': $tag = 'Contrast'; break;
+ case '002d': $tag = 'NoiseReduction'; break;
+ case '002e': $tag = 'SelfTimer'; break;
+ case '0030': $tag = 'Rotation'; break;
+ case '0032': $tag = 'ColorMode'; break;
+ case '0036': $tag = 'TravelDay'; break;
+ default: $tag = 'unknown:' . $tag; break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ * Formats Data for the data type
+ */
+ static protected function _formatData($type,$tag,$intel,$data)
+ {
+ if ($type == 'UBYTE' || $type == 'SBYTE') {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $data = hexdec($data);
+ if ($tag == '000f') { //AFMode
+ if ($data == 256) {
+ $data = _("9-area-focusing");
+ } elseif ($data == 16) {
+ $data = _("1-area-focusing");
+ } elseif ($data == 4096) {
+ $data = _("3-area-focusing (High speed)");
+ } elseif ($data == 4112) {
+ $data = _("1-area-focusing (High speed)");
+ } elseif ($data == 16) {
+ $data = _("1-area-focusing");
+ } elseif ($data == 1) {
+ $data = _("Spot-focusing");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+
+ } elseif ($type == 'URATIONAL' || $type == 'SRATIONAL') {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ $top = hexdec(substr($data, 8, 8));
+ $bottom = hexdec(substr($data, 0, 8));
+ if ($bottom!=0) {
+ $data = $top / $bottom;
+ } elseif ($top == 0) {
+ $data = 0;
+ } else {
+ $data = $top . '/' . $bottom;
+ }
+
+ } elseif ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' ||
+ $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
+
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ $data = hexdec($data);
+ if ($tag == '0001') { //Image Quality
+ if ($data == 2) {
+ $data = _("High");
+ } elseif ($data == 3) {
+ $data = _("Standard");
+ } elseif ($data == 6) {
+ $data = _("Very High");
+ } elseif ($data == 7) {
+ $data = _("RAW");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '0003') { //White Balance
+ if ($data == 1) {
+ $data = _("Auto");
+ } elseif ($data == 2) {
+ $data = _("Daylight");
+ } elseif ($data == 3) {
+ $data = _("Cloudy");
+ } elseif ($data == 4) {
+ $data = _("Halogen");
+ } elseif ($data == 5) {
+ $data = _("Manual");
+ } elseif ($data == 8) {
+ $data = _("Flash");
+ } elseif ($data == 10) {
+ $data = _("Black and White");
+ } elseif ($data == 11) {
+ $data = _("Manual");
+ } else {
+ $data = sprintf(_("Unknown(%s)"), $data);
+ }
+ }
+ if ($tag=='0007') { //Focus Mode
+ if ($data == 1) {
+ $data = _("Auto");
+ } elseif ($data == 2) {
+ $data = _("Manual");
+ } elseif ($data == 4) {
+ $data = _("Auto, Focus button");
+ } elseif ($data == 5) {
+ $data = _("Auto, Continuous");
+ } else {
+ $data = sprintf(_("Unknown(%s)"), $data);
+ }
+ }
+ if ($tag == '001a') { //Image Stabilizer
+ if ($data == 2) {
+ $data = _("Mode 1");
+ } elseif ($data == 3) {
+ $data = _("Off");
+ } elseif ($data == 4) {
+ $data = _("Mode 2");
+ } else {
+ $data = sprintf(_("Unknown(%s)"), $data);
+ }
+ }
+ if ($tag == '001c') { //Macro mode
+ if ($data == 1) {
+ $data = _("On");
+ } elseif ($data == 2) {
+ $data = _("Off");
+ } else {
+ $data = sprintf(_("Unknown(%s)"), $data);
+ }
+ }
+ if ($tag == '001f') { //Shooting Mode
+ if ($data == 1) {
+ $data = _("Normal");
+ } elseif ($data == 2) {
+ $data = _("Portrait");
+ } elseif ($data == 3) {
+ $data = _("Scenery");
+ } elseif ($data == 4) {
+ $data = _("Sports");
+ } elseif ($data == 5) {
+ $data = _("Night Portrait");
+ } elseif ($data == 6) {
+ $data = _("Program");
+ } elseif ($data == 7) {
+ $data = _("Aperture Priority");
+ } elseif ($data == 8) {
+ $data = _("Shutter Priority");
+ } elseif ($data == 9) {
+ $data = _("Macro");
+ } elseif ($data == 11) {
+ $data = _("Manual");
+ } elseif ($data == 13) {
+ $data = _("Panning");
+ } elseif ($data == 14) {
+ $data = _("Simple");
+ } elseif ($data == 18) {
+ $data = _("Fireworks");
+ } elseif ($data == 19) {
+ $data = _("Party");
+ } elseif ($data == 20) {
+ $data = _("Snow");
+ } elseif ($data == 21) {
+ $data = _("Night Scenery");
+ } elseif ($data == 22) {
+ $data = _("Food");
+ } elseif ($data == 23) {
+ $data = _("Baby");
+ } elseif ($data == 27) {
+ $data = _("High Sensitivity");
+ } elseif ($data == 29) {
+ $data = _("Underwater");
+ } elseif ($data == 33) {
+ $data = _("Pet");
+ } else {
+ $data = sprintf(_("Unknown(%s)"), $data);
+ }
+ }
+ if ($tag == '0020') { //Audio
+ if ($data == 1) {
+ $data = _("Yes");
+ } elseif ($data == 2) {
+ $data = _("No");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '0023') { //White Balance Bias
+ $data = $data . ' EV';
+ }
+ if ($tag == '0024') { //Flash Bias
+ $data = $data;
+ }
+ if ($tag == '0028') { //Colour Effect
+ if ($data == 1) {
+ $data = _("Off");
+ } elseif ($data == 2) {
+ $data = _("Warm");
+ } elseif ($data == 3) {
+ $data = _("Cool");
+ } elseif ($data == 4) {
+ $data = _("Black and White");
+ } elseif ($data == 5) {
+ $data = _("Sepia");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '002a') { //Burst Mode
+ if ($data == 0) {
+ $data = _("Off");
+ } elseif ($data == 1) {
+ $data = _("Low/High Quality");
+ } elseif ($data == 2) {
+ $data = _("Infinite");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '002c') { //Contrast
+ if ($data == 0) {
+ $data = _("Standard");
+ } elseif ($data == 1) {
+ $data = _("Low");
+ } elseif ($data == 2) {
+ $data = _("High");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '002d') { //Noise Reduction
+ if ($data == 0) {
+ $data = _("Standard");
+ } elseif ($data == 1) {
+ $data = _("Low");
+ } elseif ($data == 2) {
+ $data = _("High");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '002e') { //Self Timer
+ if ($data == 1) {
+ $data = _("Off");
+ } elseif ($data == 2) {
+ $data = _("10s");
+ } elseif ($data == 3) {
+ $data = _("2s");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '0030') { //Rotation
+ if ($data == 1) {
+ $data = _("Horizontal (normal)");
+ } elseif ($data == 6) {
+ $data = _("Rotate 90 CW");
+ } elseif ($data == 8) {
+ $data = _("Rotate 270 CW");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '0032') { //Color Mode
+ if ($data == 0) {
+ $data = _("Normal");
+ } elseif ($data == 1) {
+ $data = _("Natural");
+ } else {
+ $data = sprintf(_("Unknown (%s)"), $data);
+ }
+ }
+ if ($tag == '0036') { //Travel Day
+ $data = $data;
+ }
+ } elseif ($type != "UNDEFINED") {
+ $data = bin2hex($data);
+ if ($intel == 1) {
+ $data = Horde_Image_Exif::intel2Moto($data);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Panasonic Special data section
+ */
+ static public function parse($block, &$result)
+ {
+ $intel = 1;
+ $model = $result['IFD0']['Model'];
+ $place = 8; //current place
+ $offset = 8;
+
+ $num = bin2hex(substr($block, $place, 4));
+ $place += 4;
+
+ if ($intel == 1) {
+ $num = Horde_Image_Exif::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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ }
+ $tag_name = self::_lookupTag($tag);
+
+ //2 byte type
+ $type = bin2hex(substr($block, $place, 2));
+ $place += 2;
+ if ($intel == 1) {
+ $type = Horde_Image_Exif::intel2Moto($type);
+ }
+ self::_lookupType($type, $size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block, $place, 4));
+ $place += 4;
+ if ($intel == 1) {
+ $count = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($value);
+ }
+ $data = substr($block, hexdec($value) - $offset, $bytesofdata * 2);
+ }
+ $formated_data = self::_formatData($type, $tag, $intel, $data);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ *
+ */
+
+/*
+ Exifer
+ Extracts EXIF information from digital photos.
+
+ Copyright � 2003 Jake Olefsky
+ http://www.offsky.com/software/exif/index.php
+ jake@olefsky.com
+
+ Please see exif.php for the complete information about this software.
+
+ ------------
+
+ This program is free software; you can redistribute it and/or modify it under the terms of
+ the GNU General Public License as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
+*/
+class Horde_Image_Exif_Parser_Sanyo extends Horde_Image_Exif_Parser_Base
+{
+ /**
+ *
+ * @param $tag
+ * @return unknown_type
+ */
+ static protected function _lookupTag($tag)
+ {
+ switch($tag) {
+ case "0200": $tag = "SpecialMode";break;
+ case "0201": $tag = "Quality";break;
+ case "0202": $tag = "Macro";break;
+ case "0203": $tag = "Unknown";break;
+ case "0204": $tag = "DigiZoom";break;
+ case "0f00": $tag = "DataDump";break;
+ default: $tag = "unknown:".$tag;break;
+ }
+
+ return $tag;
+ }
+
+ /**
+ *
+ * @param $type
+ * @param $tag
+ * @param $intel
+ * @param $data
+ * @return unknown_type
+ */
+ static protected function _formatData($type,$tag,$intel,$data)
+ {
+ if($type=="ASCII") {
+
+
+ } else if($type=="URATIONAL" || $type=="SRATIONAL") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $top = hexdec(substr($data,8,8));
+ $bottom = hexdec(substr($data,0,8));
+ if($bottom!=0) $data=$top/$bottom;
+ else if($top==0) $data = 0;
+ else $data=$top."/".$bottom;
+
+
+ } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ $data=hexdec($data);
+
+ if($tag=="0200") { //SpecialMode
+ if($data == 0) $data = _("Normal");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0201") { //Quality
+ if($data == 2) $data = _("High");
+ else $data = _("Unknown").": ".$data;
+ }
+ if($tag=="0202") { //Macro
+ if($data == 0) $data = _("Normal");
+ else $data = _("Unknown").": ".$data;
+ }
+ } else if($type=="UNDEFINED") {
+
+
+
+ } else {
+ $data = bin2hex($data);
+ if($intel==1) $data = Horde_Image_Exif::intel2Moto($data);
+ }
+
+ return $data;
+ }
+
+ /**
+ *
+ * @param $block
+ * @param $result
+ * @param $seek
+ * @param $globalOffset
+ * @return unknown_type
+ */
+ static public function parse($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 = Horde_Image_Exif::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 = Horde_Image_Exif::intel2Moto($tag);
+ $tag_name = self::_lookupTag($tag);
+
+ //2 byte type
+ $type = bin2hex(substr($block,$place,2));$place+=2;
+ if($intel==1) $type = Horde_Image_Exif::intel2Moto($type);
+ self::_lookupType($type,$size);
+
+ //4 byte count of number of data units
+ $count = bin2hex(substr($block,$place,4));$place+=4;
+ if($intel==1) $count = Horde_Image_Exif::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 = Horde_Image_Exif::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 = self::_formatData($type,$tag,$intel,$data);
+ $result['SubIFD']['MakerNote'][$tag_name] = $formated_data;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * Exif driver for Horde_Image utilizing PHP's compiled-in exif functions
+ *
+ */
+class Horde_Image_Exif_Php extends Horde_Image_Exif_Base
+{
+ public function getData($image)
+ {
+ $exif = @exif_read_data($image, 0, false);
+
+ return $this->_processData($exif);
+ }
+
+}