summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/exif/lib/exif.php302
-rw-r--r--modules/exif/tests/Exif_Test.php2
2 files changed, 138 insertions, 166 deletions
diff --git a/modules/exif/lib/exif.php b/modules/exif/lib/exif.php
index f335dad4..a5dc3c74 100644
--- a/modules/exif/lib/exif.php
+++ b/modules/exif/lib/exif.php
@@ -2,30 +2,30 @@
/*
Exifer 1.6
Extracts EXIF information from digital photos.
-
+
Originally created by:
Copyright © 2005 Jake Olefsky
http:// www.offsky.com/software/exif/index.php
jake@olefsky.com
-
- 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
+
+ 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.
+ 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
-
+
SUMMARY:
This script will correctly parse all of the EXIF data included in images taken
with digital cameras. It will read the IDF0, IDF1, SubIDF and InteroperabilityIFD
fields as well as parsing some of the MakerNote fields that vary depending on
camera make and model. This script parses more tags than the internal PHP exif
implementation and it will correctly identify and decode what all the values mean.
-
- This version will correctly parse the MakerNote field for Nikon, Olympus, and Canon
+
+ This version will correctly parse the MakerNote field for Nikon, Olympus, and Canon
digital cameras. Others will follow.
-
+
TESTED WITH:
Nikon CoolPix 700
Nikon CoolPix E3200
@@ -34,7 +34,7 @@
Nikon Coolpix 5700
Canon PowerShot S200
Canon PowerShot S110
- Olympus C2040Z
+ Olympus C2040Z
Olympus C960
Olumpus E-300
Olympus E-410
@@ -58,20 +58,20 @@
Kodak DC240
Kodak DC4800
Kodak DX3215
- Ricoh RDC-5300
+ Ricoh RDC-5300
Sanyo VPC-G250
Sanyo VPC-SX550
Epson 3100z
-
+
VERSION HISTORY:
-
- 1.0 September 23, 2002
-
+
+ 1.0 September 23, 2002
+
+ First Public Release
-
+
1.1 January 25, 2003
-
+
+ Gracefully handled the error case where you pass an empty string to this library
+ Fixed an inconsistency in the Olympus Camera parsing module
+ Added support for parsing the MakerNote of Canon images.
@@ -80,12 +80,12 @@
+ Negative rational numbers are properly displayed
+ Strange old cameras that use Motorola endineness are now properly supported
+ Tested with several more cameras
-
+
Potential Problem: Negative Shorts and Negative Longs may not be correctly displayed, but I
have not yet found an example of negative shorts or longs being used.
-
+
1.2 March 30, 2003
-
+
+ Fixed an error that was displayed if you edited your image with WinXP's image viewer
+ Fixed a bug that caused some images saved from 3rd party software to not parse correctly
+ Changed the ExposureTime tag to display in fractional seconds rather than decimal
@@ -94,9 +94,9 @@
+ Added support for parsing the MakeNote of Sanyo images
+ Fixed a bug with parsing some Olympus MakerNote tags
+ Tested with several more cameras
-
+
1.3 June 15, 2003
-
+
+ Fixed Canon MakerNote support for some models
(Canon has very difficult and inconsistent MakerNote syntax)
+ Negative signed shorts and negative signed longs are properly displayed
@@ -104,17 +104,17 @@
+ More information in my comments about what each tag is
+ Parses and Displays GPS information if available
+ Tested with several more cameras
-
+
1.4 September 14, 2003
-
+
+ This software is now licensed under the GNU General Public License
+ Exposure time is now correctly displayed when the numerator is 10
- + Fixed the calculation and display of ShutterSpeedValue, ApertureValue and MaxApertureValue
+ + Fixed the calculation and display of ShutterSpeedValue, ApertureValue and MaxApertureValue
+ Fixed a bug with the GPS code
+ Tested with several more cameras
-
+
1.5 February 18, 2005
-
+
+ It now gracefully deals with a passed in file that cannot be found.
+ Fixed a GPS bug for the parsing of Altitude and other signed rational numbers
+ Defined more values for Canon cameras.
@@ -127,25 +127,25 @@
+ Now reads exif information when the jpeg markers are out of order
+ Gives raw data output for IPTC, COM and APP2 fields which are sometimes set by other applications
+ Improvements to Nikon Makernote parsing
-
+
1.6 March 25th, 2007 [Zenphoto]
-
+
+ Adopted into the Zenphoto gallery project, at http://www.zenphoto.org
+ Fixed a bug where strings had trailing null bytes.
+ Formatted selected strings better.
+ Added calculation of 35mm-equivalent focal length when possible.
+ Cleaned up code for readability and efficiency.
-
+
1.7 April 11th, 2008 [Zenphoto]
-
+
+ Fixed bug with newer Olympus cameras where number of fields was miscalculated leading to bad performance.
+ More logical fraction calculation for shutter speed.
-
+
*/
-
+
//================================================================================================
@@ -195,11 +195,11 @@ function lookup_tag($tag) {
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 '8649': $tag = 'PhotoshopSettings'; break; // ??
case '8769': $tag = 'ExifOffset'; break; // positive integer
- case '8825': $tag = 'GPSInfoOffset'; break;
+ case '8825': $tag = 'GPSInfoOffset'; break;
case '9286': $tag = 'UserCommentOld'; break; // ??
- // used by Exif SubIFD 'Image Tags'
+ // 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
@@ -260,7 +260,7 @@ function lookup_tag($tag) {
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
@@ -278,11 +278,11 @@ function lookup_tag($tag) {
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 '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;
@@ -298,7 +298,7 @@ function lookup_tag($tag) {
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;
@@ -312,7 +312,7 @@ function lookup_tag($tag) {
case '9215': $tag = 'ExposureIndex'; break;
case '9216': $tag = 'TIFF/EPStandardID'; break;
case 'a20b': $tag = 'FlashEnergy'; break;
-
+
default: $tag = 'unknown:'.$tag; break;
}
return $tag;
@@ -358,18 +358,18 @@ function formatData($type,$tag,$intel,$data) {
} else if ($type == 'URATIONAL' || $type == 'SRATIONAL') {
$data = bin2hex($data);
if ($intel == 1) $data = 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) $top = $top - 4294967296; // this makes the number signed instead of unsigned
if ($bottom != 0) $data=$top/$bottom;
else if ($top == 0) $data = 0;
else $data = $top.'/'.$bottom;
-
+
if (($tag == '011a' || $tag == '011b') && $bottom == 1) { // XResolution YResolution
$data = $top.' dots per ResolutionUnit';
} else if ($tag == '829a') { // Exposure Time
@@ -388,7 +388,7 @@ function formatData($type,$tag,$intel,$data) {
// The formula is : Aperture = 2*log2(FNumber) <=> FNumber = e((Aperture.ln(2))/2)
$data = exp(($data*log(2))/2);
$data = round($data, 1);// Focal is given with a precision of 1 digit.
- $data='f/'.$data;
+ $data='f/'.$data;
} else if ($tag == '920a') { // FocalLength
$data = $data.' mm';
} else if ($tag == '9201') { // ShutterSpeedValue
@@ -400,17 +400,17 @@ function formatData($type,$tag,$intel,$data) {
$data = exp($data * log(2));
if ($data != 0) $data = 1/$data;
$data = formatExposure($data);
- }
-
+ }
+
} else if ($type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' || $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
$data = bin2hex($data);
if ($intel == 1) $data = intel2Moto($data);
if ($intel == 0 && ($type == 'USHORT' || $type == 'SSHORT')) $data = substr($data,0,4);
$data = hexdec($data);
-
+
if ($type == 'SSHORT' && $data > 32767) $data = $data - 65536; // this makes the number signed instead of unsigned
if ($type == 'SLONG' && $data > 2147483647) $data = $data - 4294967296; // this makes the number signed instead of unsigned
-
+
if ($tag == '0112') { // Orientation
// Example of how all of these tag formatters should be...
switch ($data) {
@@ -424,16 +424,16 @@ function formatData($type,$tag,$intel,$data) {
case 8 : $data = gettext('8: 90 deg CW'); break;
default : $data = gettext('Unknown').': '.$data;
}
-
+
} else if ($tag == '0128' || $tag == 'a210' || $tag == '0128') { // ResolutionUnit and FocalPlaneResolutionUnit and ThumbnailResolutionUnit
if ($data == 1) $data = gettext('No Unit');
else if ($data == 2) $data = gettext('Inch');
else if ($data == 3) $data = gettext('Centimeter');
-
+
} else if ($tag == '0213') { // YCbCrPositioning
if ($data == 1) $data = gettext('Center of Pixel Array');
else if ($data == 2) $data = gettext('Datum Point');
-
+
} else if ($tag == '8822') { // ExposureProgram
if ($data == 1) $data = gettext('Manual');
else if ($data == 2) $data = gettext('Program');
@@ -444,7 +444,7 @@ function formatData($type,$tag,$intel,$data) {
else if ($data == 7) $data = gettext('Portrat');
else if ($data == 8) $data = gettext('Landscape');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == '9207') { // MeteringMode
if ($data == 0) $data = gettext('Unknown');
else if ($data == 1) $data = gettext('Average');
@@ -455,7 +455,7 @@ function formatData($type,$tag,$intel,$data) {
else if ($data == 6) $data = gettext('Partial');
else if ($data == 255) $data = gettext('Other');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == '9208') { // LightSource
if ($data == 0) $data = gettext('Unknown or Auto');
else if ($data == 1) $data = gettext('Daylight');
@@ -480,7 +480,7 @@ function formatData($type,$tag,$intel,$data) {
else if ($data == 24) $data = gettext('ISO Studio Tungsten');
else if ($data == 255) $data = gettext('Other');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == '9209') { // Flash
if ($data == 0) $data = gettext('No Flash');
else if ($data == 1) $data = gettext('Flash');
@@ -505,19 +505,19 @@ function formatData($type,$tag,$intel,$data) {
else if ($data == 93) $data = gettext('Red Eye, Auto-Mode, Return light not detected');
else if ($data == 95) $data = gettext('Red Eye, Auto-Mode, Return light detected');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == 'a001') { // ColorSpace
if ($data == 1) $data = gettext('sRGB');
else $data = gettext('Uncalibrated');
-
+
} else if ($tag == 'a002' || $tag == 'a003') { // ExifImageWidth/Height
$data = $data. ' '.gettext('pixels');
-
+
} else if ($tag == '0103') { // Compression
if ($data == 1) $data = gettext('No Compression');
else if ($data == 6) $data = gettext('Jpeg Compression');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == 'a217') { // SensingMethod
if ($data == 1) $data = gettext('Not defined');
if ($data == 2) $data = gettext('One Chip Color Area Sensor');
@@ -527,7 +527,7 @@ function formatData($type,$tag,$intel,$data) {
if ($data == 7) $data = gettext('Trilinear Sensor');
if ($data == 8) $data = gettext('Color Sequential Linear Sensor');
else $data = gettext('Unknown').': '.$data;
-
+
} else if ($tag == '0106') { // PhotometricInterpretation
if ($data == 1) $data = gettext('Monochrome');
else if ($data == 2) $data = gettext('RGB');
@@ -555,9 +555,9 @@ function formatData($type,$tag,$intel,$data) {
// case 2: $data="Auto bracket"; break;
// default: $data="Unknown"; break;
// }
-
+
} else if ($type == 'UNDEFINED') {
-
+
if ($tag == '9000' || $tag == 'a000' || $tag == '0002') { // ExifVersion,FlashPixVersion,InteroperabilityVersion
$data=gettext('version').' '.$data/100;
}
@@ -589,7 +589,7 @@ function formatData($type,$tag,$intel,$data) {
$data = bin2hex($data);
if ($intel == 1) $data = intel2Moto($data);
}
-
+
return $data;
}
@@ -611,35 +611,35 @@ function formatExposure($data) {
// Reads one standard IFD entry
//================================================================================================
function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
-
+
if (feof($in)) { // test to make sure we can still read.
$result['Errors'] = $result['Errors']+1;
return;
}
-
+
// 2 byte tag
$tag = bin2hex(fread($in, 2));
if ($intel == 1) $tag = intel2Moto($tag);
$tag_name = lookup_tag($tag);
-
+
// 2 byte datatype
$type = bin2hex(fread($in, 2));
if ($intel == 1) $type = intel2Moto($type);
lookup_type($type, $size);
-
+
if (strpos($tag_name, 'unknown:') !== false && strpos($type, 'error:') !== false) { // we have an error
$result['Errors'] = $result['Errors']+1;
return;
}
-
+
// 4 byte number of elements
$count = bin2hex(fread($in, 4));
if ($intel == 1) $count = intel2Moto($count);
$bytesofdata = $size*hexdec($count);
-
+
// 4 byte value or pointer to value if larger than 4 bytes
$value = fread( $in, 4 );
-
+
if ($bytesofdata <= 4) { // if datatype is 4 bytes or less, its the value
$data = $value;
} else if ($bytesofdata < 100000) { // otherwise its a pointer to the value, so lets go get it
@@ -657,7 +657,7 @@ function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
}
if ($tag_name == 'MakerNote') { // if its a maker tag, we need to parse this specially
$make = $result['IFD0']['Make'];
-
+
if ($result['VerboseOutput'] == 1) {
$result[$ifd_name]['MakerNote']['RawData'] = $data;
}
@@ -681,10 +681,10 @@ function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
require_once(dirname(__FILE__).'/makers/sanyo.php');
parseSanyo($data,$result,$seek,$globalOffset);
$result[$ifd_name]['KnownMaker'] = 1;
- } else if (eregi('Panasonic',$make)) {
- require_once(dirname(__FILE__).'/makers/panasonic.php');
- parsePanasonic($data,$result,$seek,$globalOffset);
- $result[$ifd_name]['KnownMaker'] = 1;
+ } else if (eregi('Panasonic',$make)) {
+ require_once(dirname(__FILE__).'/makers/panasonic.php');
+ parsePanasonic($data,$result,$seek,$globalOffset);
+ $result[$ifd_name]['KnownMaker'] = 1;
} else {
$result[$ifd_name]['KnownMaker'] = 0;
}
@@ -696,9 +696,9 @@ function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
} else {
// Format the data depending on the type and tag
$formated_data = formatData($type,$tag,$intel,$data);
-
+
$result[$ifd_name][$tag_name] = $formated_data;
-
+
if ($result['VerboseOutput'] == 1) {
if ($type == 'URATIONAL' || $type == 'SRATIONAL' || $type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' || $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
$data = bin2hex($data);
@@ -725,19 +725,19 @@ function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
// http:// us2.php.net/manual/en/function.exif-read-data.php
//================================================================================================
function read_exif_data_raw($path,$verbose) {
-
+
if ($path == '' || $path == 'none') return;
-
+
$in = @fopen($path, 'rb'); // the b is for windows machines to open in binary mode
$seek = @fopen($path, 'rb'); // There may be an elegant way to do this with one file handle.
-
+
$globalOffset = 0;
-
+
if (!isset($verbose)) $verbose=0;
-
+
$result['VerboseOutput'] = $verbose;
$result['Errors'] = 0;
-
+
if (!$in || !$seek) { // if the path was invalid, this error will catch it
$result['Errors'] = 1;
$result['Error'][$result['Errors']] = gettext('The file could not be found.');
@@ -745,22 +745,22 @@ function read_exif_data_raw($path,$verbose) {
}
$GLOBALS['exiferFileSize'] = filesize($path);
-
- // First 2 bytes of JPEG are 0xFFD8
+
+ // First 2 bytes of JPEG are 0xFFD8
$data = bin2hex(fread( $in, 2 ));
if ($data == 'ffd8') {
$result['ValidJpeg'] = 1;
} else {
$result['ValidJpeg'] = 0;
fseek($in, 0);
- }
-
+ }
+
$result['ValidIPTCData'] = 0;
$result['ValidJFIFData'] = 0;
$result['ValidEXIFData'] = 0;
$result['ValidAPP2Data'] = 0;
$result['ValidCOMData'] = 0;
-
+
if ($result['ValidJpeg'] == 1) {
// Next 2 bytes are MARKER tag (0xFFE#)
$data = bin2hex(fread( $in, 2 ));
@@ -772,56 +772,56 @@ if ($result['ValidJpeg'] == 1) {
if ($data == 'ffe0') { // JFIF Marker
$result['ValidJFIFData'] = 1;
$result['JFIF']['Size'] = hexdec($size);
-
+
if (hexdec($size)-2 > 0) {
$data = fread( $in, hexdec($size)-2);
$result['JFIF']['Data'] = $data;
- }
-
+ }
+
$result['JFIF']['Identifier'] = substr($data,0,5);;
$result['JFIF']['ExtensionCode'] = bin2hex(substr($data,6,1));
-
+
$globalOffset+=hexdec($size)+2;
-
+
} else if ($data == 'ffed') { // IPTC Marker
$result['ValidIPTCData'] = 1;
$result['IPTC']['Size'] = hexdec($size);
-
+
if (hexdec($size)-2 > 0) {
$data = fread( $in, hexdec($size)-2);
$result['IPTC']['Data'] = $data ;
- }
+ }
$globalOffset+=hexdec($size)+2;
-
+
} else if ($data == 'ffe2') { // EXIF extension Marker
$result['ValidAPP2Data'] = 1;
$result['APP2']['Size'] = hexdec($size);
-
+
if (hexdec($size)-2 > 0) {
$data = fread( $in, hexdec($size)-2);
$result['APP2']['Data'] = $data ;
- }
+ }
$globalOffset+=hexdec($size)+2;
-
+
} else if ($data == 'fffe') { // COM extension Marker
$result['ValidCOMData'] = 1;
$result['COM']['Size'] = hexdec($size);
-
+
if (hexdec($size)-2 > 0) {
$data = fread( $in, hexdec($size)-2);
$result['COM']['Data'] = $data ;
- }
+ }
$globalOffset+=hexdec($size)+2;
-
+
} else if ($data == 'ffe1') {
$result['ValidEXIFData'] = 1;
}
-
+
$data = bin2hex(fread( $in, 2 ));
$size = bin2hex(fread( $in, 2 ));
}
// END MARKER LOOP
-
+
if ($data == 'ffe1') {
$result['ValidEXIFData'] = 1;
} else {
@@ -829,16 +829,16 @@ if ($result['ValidJpeg'] == 1) {
fclose($seek);
return $result;
}
-
- // Size of APP1
+
+ // Size of APP1
$result['APP1Size'] = hexdec($size);
-
+
// Start of APP1 block starts with 'Exif' header (6 bytes)
$header = fread( $in, 6 );
} // END IF ValidJpeg
-
- // Then theres a TIFF header with 2 bytes of endieness (II or MM)
+
+ // Then theres a TIFF header with 2 bytes of endieness (II or MM)
$header = fread( $in, 2 );
if ($header==='II') {
$intel=1;
@@ -850,14 +850,14 @@ if ($result['ValidJpeg'] == 1) {
$intel=1; // not sure what the default should be, but this seems reasonable
$result['Endien'] = 'Unknown';
}
-
+
// 2 bytes of 0x002a
$tag = bin2hex(fread( $in, 2 ));
-
+
// Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header)
$offset = bin2hex(fread( $in, 4 ));
if ($intel == 1) $offset = intel2Moto($offset);
-
+
// Check for extremely large values here
if (hexdec($offset) > 100000) {
$result['ValidEXIFData'] = 0;
@@ -867,20 +867,20 @@ if ($result['ValidJpeg'] == 1) {
}
if (hexdec($offset)>8) $unknown = fread( $in, hexdec($offset)-8); // fixed this bug in 1.3
-
+
// add 12 to the offset to account for TIFF header
if ($result['ValidJpeg'] == 1) {
$globalOffset+=12;
}
-
-
+
+
//===========================================================
// Start of IFD0
$num = bin2hex(fread( $in, 2 ));
if ($intel == 1) $num = intel2Moto($num);
$num = hexdec($num);
$result['IFD0NumTags'] = $num;
-
+
if ($num<1000) { // 1000 entries is too much and is probably an error.
for($i=0; $i<$num; $i++) {
read_entry($result,$in,$seek,$intel,'IFD0',$globalOffset);
@@ -889,19 +889,19 @@ if ($result['ValidJpeg'] == 1) {
$result['Errors'] = $result['Errors']+1;
$result['Error'][$result['Errors']] = 'Illegal size for IFD0';
}
-
+
// store offset to IFD1
$offset = bin2hex(fread( $in, 4 ));
if ($intel == 1) $offset = intel2Moto($offset);
$result['IFD1Offset'] = hexdec($offset);
-
+
// Check for SubIFD
if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) {
fclose($in);
fclose($seek);
return $result;
}
-
+
// seek to SubIFD (Value of ExifOffset tag) above.
$ExitOffset = $result['IFD0']['ExifOffset'];
$v = fseek($in,$globalOffset+$ExitOffset);
@@ -909,14 +909,14 @@ if ($result['ValidJpeg'] == 1) {
$result['Errors'] = $result['Errors']+1;
$result['Error'][$result['Errors']] = gettext('Couldnt Find SubIFD');
}
-
+
//===========================================================
// Start of SubIFD
$num = bin2hex(fread( $in, 2 ));
if ($intel == 1) $num = intel2Moto($num);
$num = hexdec($num);
$result['SubIFDNumTags'] = $num;
-
+
if ($num<1000) { // 1000 entries is too much and is probably an error.
for($i=0; $i<$num; $i++) {
read_entry($result,$in,$seek,$intel,'SubIFD',$globalOffset);
@@ -925,10 +925,10 @@ if ($result['ValidJpeg'] == 1) {
$result['Errors'] = $result['Errors']+1;
$result['Error'][$result['Errors']] = gettext('Illegal size for SubIFD');
}
-
+
// Add the 35mm equivalent focal length:
$result['SubIFD']['FocalLength35mmEquiv'] = get35mmEquivFocalLength($result);
-
+
// Check for IFD1
if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) {
fclose($in);
@@ -941,14 +941,14 @@ if ($result['ValidJpeg'] == 1) {
$result['Errors'] = $result['Errors']+1;
$result['Error'][$result['Errors']] = gettext('Couldnt Find IFD1');
}
-
+
//===========================================================
// Start of IFD1
$num = bin2hex(fread( $in, 2 ));
if ($intel == 1) $num = intel2Moto($num);
$num = hexdec($num);
$result['IFD1NumTags'] = $num;
-
+
if ($num<1000) { // 1000 entries is too much and is probably an error.
for($i=0; $i<$num; $i++) {
read_entry($result,$in,$seek,$intel,'IFD1',$globalOffset);
@@ -966,9 +966,9 @@ if ($result['ValidJpeg'] == 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);
@@ -981,14 +981,14 @@ if ($result['ValidJpeg'] == 1) {
$result['Errors'] = $result['Errors']+1;
$result['Error'][$result['Errors']] = gettext('Couldnt Find InteroperabilityIFD');
}
-
+
//===========================================================
// Start of InteroperabilityIFD
$num = bin2hex(fread( $in, 2 ));
if ($intel == 1) $num = intel2Moto($num);
$num = hexdec($num);
$result['InteroperabilityIFDNumTags'] = $num;
-
+
if ($num<1000) { // 1000 entries is too much and is probably an error.
for($i=0; $i<$num; $i++) {
read_entry($result,$in,$seek,$intel,'InteroperabilityIFD',$globalOffset);
@@ -1007,37 +1007,9 @@ if ($result['ValidJpeg'] == 1) {
//================================================================================================
function ConvertToFraction($v, &$n, &$d)
{
- $MaxTerms = 15; // Limit to prevent infinite loop
- $MinDivisor = 0.000001; // Limit to prevent divide by zero
- $MaxError = 0.00000001; // How close is enough
-
- $f = $v; // Initialize fraction being converted
-
- $n_un = 1; // Initialize fractions with 1/0, 0/1
- $d_un = 0;
- $n_deux = 0;
- $d_deux = 1;
-
- for ($i = 0; $i<$MaxTerms; $i++)
- {
- $a = floor($f); // Get next term
- $f = $f - $a; // Get new divisor
- $n = $n_un * $a + $n_deux; // Calculate new fraction
- $d = $d_un * $a + $d_deux;
- $n_deux = $n_un; // Save last two fractions
- $d_deux = $d_un;
- $n_un = $n;
- $d_un = $d;
-
- if ($f < $MinDivisor) // Quit if dividing by zero
- break;
-
- if (abs($v - $n / $d) < $MaxError)
- break;
-
- $f = 1 / $f; // Take reciprocal
- }
-}
+ $n = 1;
+ $d = !empty($v) ? round(1.0 / $v) : 0;
+}
//================================================================================================
// Calculates the 35mm-equivalent focal length from the reported sensor resolution, by Tristan Harward.
@@ -1070,7 +1042,7 @@ function get35mmEquivFocalLength(&$result) {
} else {
$fl = 0;
}
-
+
if (($width != 0) && !empty($units) && !empty($xres) && !empty($fl) && !empty($width)) {
$ccdwidth = ($width * $unitfactor) / $xres;
$equivfl = $fl / $ccdwidth*36+0.5;
diff --git a/modules/exif/tests/Exif_Test.php b/modules/exif/tests/Exif_Test.php
index f94d0357..5c5a5cb1 100644
--- a/modules/exif/tests/Exif_Test.php
+++ b/modules/exif/tests/Exif_Test.php
@@ -31,7 +31,7 @@ class Exif_Test extends Unit_Test_Case {
array("caption" => "Color Space", "value" => "Uncalibrated"),
array("caption" => "Exposure Value", "value" => "4294.67 EV"),
array("caption" => "Exposure Program", "value" => "Program"),
- array("caption" => "Exposure Time", "value" => "833/49979 sec"),
+ array("caption" => "Exposure Time", "value" => "1/60 sec"),
array("caption" => "Flash", "value" => "No Flash"),
array("caption" => "Focal Length", "value" => "50 mm"),
array("caption" => "ISO", "value" => "6553700"),