PageRenderTime 61ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/classes-old/tmp/exifer-tmp/exif.php

https://github.com/davscro/WordPress-Basis-Theme
PHP | 1134 lines | 779 code | 88 blank | 267 comment | 229 complexity | 36df5b6d14ef6b3ea52a1667469337a1 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /*
  3. Exifer 1.6
  4. Extracts EXIF information from digital photos.
  5. Originally created by:
  6. Copyright Š 2005 Jake Olefsky
  7. http:// www.offsky.com/software/exif/index.php
  8. jake@olefsky.com
  9. This program is free software; you can redistribute it and/or modify it under the terms of
  10. the GNU General Public License as published by the Free Software Foundation; either version 2
  11. of the License, or (at your option) any later version.
  12. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  13. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. See the GNU General Public License for more details. http:// www.gnu.org/copyleft/gpl.html
  15. SUMMARY:
  16. This script will correctly parse all of the EXIF data included in images taken
  17. with digital cameras. It will read the IDF0, IDF1, SubIDF and InteroperabilityIFD
  18. fields as well as parsing some of the MakerNote fields that vary depending on
  19. camera make and model. This script parses more tags than the internal PHP exif
  20. implementation and it will correctly identify and decode what all the values mean.
  21. This version will correctly parse the MakerNote field for Nikon, Olympus, and Canon
  22. digital cameras. Others will follow.
  23. TESTED WITH:
  24. Nikon CoolPix 700
  25. Nikon CoolPix E3200
  26. Nikon CoolPix 4500
  27. Nikon CoolPix 950
  28. Nikon Coolpix 5700
  29. Canon PowerShot S200
  30. Canon PowerShot S110
  31. Olympus C2040Z
  32. Olympus C960
  33. Olumpus E-300
  34. Olympus E-410
  35. Olympus E-500
  36. Olympus E-510
  37. Olympus E-3
  38. Canon Ixus
  39. Canon EOS 300D
  40. Canon Digital Rebel
  41. Canon EOS 10D
  42. Canon PowerShot G2
  43. FujiFilm DX 10
  44. FujiFilm MX-1200
  45. FujiFilm FinePix2400
  46. FujiFilm FinePix2600
  47. FujiFilm FinePix S602
  48. FujiFilm FinePix40i
  49. Sony D700
  50. Sony Cybershot
  51. Kodak DC210
  52. Kodak DC240
  53. Kodak DC4800
  54. Kodak DX3215
  55. Ricoh RDC-5300
  56. Sanyo VPC-G250
  57. Sanyo VPC-SX550
  58. Epson 3100z
  59. VERSION HISTORY:
  60. 1.0 September 23, 2002
  61. + First Public Release
  62. 1.1 January 25, 2003
  63. + Gracefully handled the error case where you pass an empty string to this library
  64. + Fixed an inconsistency in the Olympus Camera parsing module
  65. + Added support for parsing the MakerNote of Canon images.
  66. + Modified how the imagefile is opened so it works for windows machines.
  67. + Correctly parses the FocalPlaneResolutionUnit and PhotometricInterpretation fields
  68. + Negative rational numbers are properly displayed
  69. + Strange old cameras that use Motorola endineness are now properly supported
  70. + Tested with several more cameras
  71. Potential Problem: Negative Shorts and Negative Longs may not be correctly displayed, but I
  72. have not yet found an example of negative shorts or longs being used.
  73. 1.2 March 30, 2003
  74. + Fixed an error that was displayed if you edited your image with WinXP's image viewer
  75. + Fixed a bug that caused some images saved from 3rd party software to not parse correctly
  76. + Changed the ExposureTime tag to display in fractional seconds rather than decimal
  77. + Updated the ShutterSpeedValue tag to have the units of 'sec'
  78. + Added support for parsing the MakeNote of FujiFilm images
  79. + Added support for parsing the MakeNote of Sanyo images
  80. + Fixed a bug with parsing some Olympus MakerNote tags
  81. + Tested with several more cameras
  82. 1.3 June 15, 2003
  83. + Fixed Canon MakerNote support for some models
  84. (Canon has very difficult and inconsistent MakerNote syntax)
  85. + Negative signed shorts and negative signed longs are properly displayed
  86. + Several more tags are defined
  87. + More information in my comments about what each tag is
  88. + Parses and Displays GPS information if available
  89. + Tested with several more cameras
  90. 1.4 September 14, 2003
  91. + This software is now licensed under the GNU General Public License
  92. + Exposure time is now correctly displayed when the numerator is 10
  93. + Fixed the calculation and display of ShutterSpeedValue, ApertureValue and MaxApertureValue
  94. + Fixed a bug with the GPS code
  95. + Tested with several more cameras
  96. 1.5 February 18, 2005
  97. + It now gracefully deals with a passed in file that cannot be found.
  98. + Fixed a GPS bug for the parsing of Altitude and other signed rational numbers
  99. + Defined more values for Canon cameras.
  100. + Added 'bulb' detection for ShutterSpeed
  101. + Made script loading a little faster and less memory intensive.
  102. + Bug fixes
  103. + Better error reporting
  104. + Graceful failure for files with corrupt exif info.
  105. + QuickTime (including iPhoto) messes up the Makernote tag for certain photos (no workaround yet)
  106. + Now reads exif information when the jpeg markers are out of order
  107. + Gives raw data output for IPTC, COM and APP2 fields which are sometimes set by other applications
  108. + Improvements to Nikon Makernote parsing
  109. 1.6 March 25th, 2007 [Zenphoto]
  110. + Adopted into the Zenphoto gallery project, at http://www.zenphoto.org
  111. + Fixed a bug where strings had trailing null bytes.
  112. + Formatted selected strings better.
  113. + Added calculation of 35mm-equivalent focal length when possible.
  114. + Cleaned up code for readability and efficiency.
  115. 1.7 April 11th, 2008 [Zenphoto]
  116. + Fixed bug with newer Olympus cameras where number of fields was miscalculated leading to bad performance.
  117. + More logical fraction calculation for shutter speed.
  118. 2009: For all further changes, see the Zenphoto change logs.
  119. */
  120. //================================================================================================
  121. // Converts from Intel to Motorola endien. Just reverses the bytes (assumes hex is passed in)
  122. //================================================================================================
  123. function intel2Moto($intel) {
  124. static $cache = array();
  125. if (isset($cache[$intel])) {
  126. return $cache[$intel];
  127. }
  128. $cache[$intel] = '';
  129. $len = strlen($intel);
  130. if ($len > 1000) { // an unreasonable length, override it.
  131. $len = 1000;
  132. }
  133. for($i = 0; $i <= $len; $i += 2) {
  134. $cache[$intel] .= substr($intel, $len-$i, 2);
  135. }
  136. return $cache[$intel];
  137. }
  138. //================================================================================================
  139. // Looks up the name of the tag
  140. //================================================================================================
  141. function lookup_tag($tag) {
  142. switch($tag) {
  143. // used by IFD0 'Camera Tags'
  144. case '000b': $tag = 'ACDComment'; break; // text string up to 999 bytes long
  145. case '00fe': $tag = 'ImageType'; break; // integer -2147483648 to 2147483647
  146. case '0106': $tag = 'PhotometricInterpret'; break; // ?? Please send sample image with this tag
  147. case '010e': $tag = 'ImageDescription'; break; // text string up to 999 bytes long
  148. case '010f': $tag = 'Make'; break; // text string up to 999 bytes long
  149. case '0110': $tag = 'Model'; break; // text string up to 999 bytes long
  150. case '0112': $tag = 'Orientation'; break; // integer values 1-9
  151. case '0115': $tag = 'SamplePerPixel'; break; // integer 0-65535
  152. case '011a': $tag = 'xResolution'; break; // positive rational number
  153. case '011b': $tag = 'yResolution'; break; // positive rational number
  154. case '011c': $tag = 'PlanarConfig'; break; // integer values 1-2
  155. case '0128': $tag = 'ResolutionUnit'; break; // integer values 1-3
  156. case '0131': $tag = 'Software'; break; // text string up to 999 bytes long
  157. case '0132': $tag = 'DateTime'; break; // YYYY:MM:DD HH:MM:SS
  158. case '013b': $tag = 'Artist'; break; // text string up to 999 bytes long
  159. case '013c': $tag = 'HostComputer'; break; // text string
  160. case '013e': $tag = 'WhitePoint'; break; // two positive rational numbers
  161. case '013f': $tag = 'PrimaryChromaticities'; break; // six positive rational numbers
  162. case '0211': $tag = 'YCbCrCoefficients'; break; // three positive rational numbers
  163. case '0213': $tag = 'YCbCrPositioning'; break; // integer values 1-2
  164. case '0214': $tag = 'ReferenceBlackWhite'; break; // six positive rational numbers
  165. case '8298': $tag = 'Copyright'; break; // text string up to 999 bytes long
  166. case '8649': $tag = 'PhotoshopSettings'; break; // ??
  167. case '8769': $tag = 'ExifOffset'; break; // positive integer
  168. case '8825': $tag = 'GPSInfoOffset'; break;
  169. case '9286': $tag = 'UserCommentOld'; break; // ??
  170. // used by Exif SubIFD 'Image Tags'
  171. case '829a': $tag = 'ExposureTime'; break; // seconds or fraction of seconds 1/x
  172. case '829d': $tag = 'FNumber'; break; // positive rational number
  173. case '8822': $tag = 'ExposureProgram'; break; // integer value 1-9
  174. case '8824': $tag = 'SpectralSensitivity'; break; // ??
  175. case '8827': $tag = 'ISOSpeedRatings'; break; // integer 0-65535
  176. case '9000': $tag = 'ExifVersion'; break; // ??
  177. case '9003': $tag = 'DateTimeOriginal'; break; // YYYY:MM:DD HH:MM:SS
  178. case '9004': $tag = 'DateTimeDigitized'; break; // YYYY:MM:DD HH:MM:SS
  179. case '9101': $tag = 'ComponentsConfiguration'; break; // ??
  180. case '9102': $tag = 'CompressedBitsPerPixel'; break; // positive rational number
  181. case '9201': $tag = 'ShutterSpeedValue'; break; // seconds or fraction of seconds 1/x
  182. case '9202': $tag = 'ApertureValue'; break; // positive rational number
  183. case '9203': $tag = 'BrightnessValue'; break; // positive rational number
  184. case '9204': $tag = 'ExposureBiasValue'; break; // positive rational number (EV)
  185. case '9205': $tag = 'MaxApertureValue'; break; // positive rational number
  186. case '9206': $tag = 'SubjectDistance'; break; // positive rational number (meters)
  187. case '9207': $tag = 'MeteringMode'; break; // integer 1-6 and 255
  188. case '9208': $tag = 'LightSource'; break; // integer 1-255
  189. case '9209': $tag = 'Flash'; break; // integer 1-255
  190. case '920a': $tag = 'FocalLength'; break; // positive rational number (mm)
  191. case '9213': $tag = 'ImageHistory'; break; // text string up to 999 bytes long
  192. case '927c': $tag = 'MakerNote'; break; // a bunch of data
  193. case '9286': $tag = 'UserComment'; break; // text string
  194. case '9290': $tag = 'SubsecTime'; break; // text string up to 999 bytes long
  195. case '9291': $tag = 'SubsecTimeOriginal'; break; // text string up to 999 bytes long
  196. case '9292': $tag = 'SubsecTimeDigitized'; break; // text string up to 999 bytes long
  197. case 'a000': $tag = 'FlashPixVersion'; break; // ??
  198. case 'a001': $tag = 'ColorSpace'; break; // values 1 or 65535
  199. case 'a002': $tag = 'ExifImageWidth'; break; // ingeter 1-65535
  200. case 'a003': $tag = 'ExifImageHeight'; break; // ingeter 1-65535
  201. case 'a004': $tag = 'RelatedSoundFile'; break; // text string 12 bytes long
  202. case 'a005': $tag = 'ExifInteroperabilityOffset'; break; // positive integer
  203. case 'a20c': $tag = 'SpacialFreqResponse'; break; // ??
  204. case 'a20b': $tag = 'FlashEnergy'; break; // positive rational number
  205. case 'a20e': $tag = 'FocalPlaneXResolution'; break; // positive rational number
  206. case 'a20f': $tag = 'FocalPlaneYResolution'; break; // positive rational number
  207. case 'a210': $tag = 'FocalPlaneResolutionUnit'; break; // values 1-3
  208. case 'a214': $tag = 'SubjectLocation'; break; // two integers 0-65535
  209. case 'a215': $tag = 'ExposureIndex'; break; // positive rational number
  210. case 'a217': $tag = 'SensingMethod'; break; // values 1-8
  211. case 'a300': $tag = 'FileSource'; break; // integer
  212. case 'a301': $tag = 'SceneType'; break; // integer
  213. case 'a302': $tag = 'CFAPattern'; break; // undefined data type
  214. case 'a401': $tag = 'CustomerRender'; break; // values 0 or 1
  215. case 'a402': $tag = 'ExposureMode'; break; // values 0-2
  216. case 'a403': $tag = 'WhiteBalance'; break; // values 0 or 1
  217. case 'a404': $tag = 'DigitalZoomRatio'; break; // positive rational number
  218. case 'a405': $tag = 'FocalLengthIn35mmFilm'; break;
  219. case 'a406': $tag = 'SceneCaptureMode'; break; // values 0-3
  220. case 'a407': $tag = 'GainControl'; break; // values 0-4
  221. case 'a408': $tag = 'Contrast'; break; // values 0-2
  222. case 'a409': $tag = 'Saturation'; break; // values 0-2
  223. case 'a40a': $tag = 'Sharpness'; break; // values 0-2
  224. // used by Interoperability IFD
  225. case '0001': $tag = 'InteroperabilityIndex'; break; // text string 3 bytes long
  226. case '0002': $tag = 'InteroperabilityVersion'; break; // datatype undefined
  227. case '1000': $tag = 'RelatedImageFileFormat'; break; // text string up to 999 bytes long
  228. case '1001': $tag = 'RelatedImageWidth'; break; // integer in range 0-65535
  229. case '1002': $tag = 'RelatedImageLength'; break; // integer in range 0-65535
  230. // used by IFD1 'Thumbnail'
  231. case '0100': $tag = 'ImageWidth'; break; // integer in range 0-65535
  232. case '0101': $tag = 'ImageLength'; break; // integer in range 0-65535
  233. case '0102': $tag = 'BitsPerSample'; break; // integers in range 0-65535
  234. case '0103': $tag = 'Compression'; break; // values 1 or 6
  235. case '0106': $tag = 'PhotometricInterpretation'; break;// values 0-4
  236. case '010e': $tag = 'ThumbnailDescription'; break; // text string up to 999 bytes long
  237. case '010f': $tag = 'ThumbnailMake'; break; // text string up to 999 bytes long
  238. case '0110': $tag = 'ThumbnailModel'; break; // text string up to 999 bytes long
  239. case '0111': $tag = 'StripOffsets'; break; // ??
  240. case '0112': $tag = 'ThumbnailOrientation'; break; // integer 1-9
  241. case '0115': $tag = 'SamplesPerPixel'; break; // ??
  242. case '0116': $tag = 'RowsPerStrip'; break; // ??
  243. case '0117': $tag = 'StripByteCounts'; break; // ??
  244. case '011a': $tag = 'ThumbnailXResolution'; break; // positive rational number
  245. case '011b': $tag = 'ThumbnailYResolution'; break; // positive rational number
  246. case '011c': $tag = 'PlanarConfiguration'; break; // values 1 or 2
  247. case '0128': $tag = 'ThumbnailResolutionUnit'; break; // values 1-3
  248. case '0201': $tag = 'JpegIFOffset'; break;
  249. case '0202': $tag = 'JpegIFByteCount'; break;
  250. case '0212': $tag = 'YCbCrSubSampling'; break;
  251. // misc
  252. case '00ff': $tag = 'SubfileType'; break;
  253. case '012d': $tag = 'TransferFunction'; break;
  254. case '013d': $tag = 'Predictor'; break;
  255. case '0142': $tag = 'TileWidth'; break;
  256. case '0143': $tag = 'TileLength'; break;
  257. case '0144': $tag = 'TileOffsets'; break;
  258. case '0145': $tag = 'TileByteCounts'; break;
  259. case '014a': $tag = 'SubIFDs'; break;
  260. case '015b': $tag = 'JPEGTables'; break;
  261. case '828d': $tag = 'CFARepeatPatternDim'; break;
  262. case '828e': $tag = 'CFAPattern'; break;
  263. case '828f': $tag = 'BatteryLevel'; break;
  264. case '83bb': $tag = 'IPTC/NAA'; break;
  265. case '8773': $tag = 'InterColorProfile'; break;
  266. case '8828': $tag = 'OECF'; break;
  267. case '8829': $tag = 'Interlace'; break;
  268. case '882a': $tag = 'TimeZoneOffset'; break;
  269. case '882b': $tag = 'SelfTimerMode'; break;
  270. case '920b': $tag = 'FlashEnergy'; break;
  271. case '920c': $tag = 'SpatialFrequencyResponse'; break;
  272. case '920d': $tag = 'Noise'; break;
  273. case '9211': $tag = 'ImageNumber'; break;
  274. case '9212': $tag = 'SecurityClassification'; break;
  275. case '9214': $tag = 'SubjectLocation'; break;
  276. case '9215': $tag = 'ExposureIndex'; break;
  277. case '9216': $tag = 'TIFF/EPStandardID'; break;
  278. case 'a20b': $tag = 'FlashEnergy'; break;
  279. default: $tag = 'unknown:'.$tag; break;
  280. }
  281. return $tag;
  282. }
  283. //================================================================================================
  284. // Looks up the datatype
  285. //================================================================================================
  286. function lookup_type(&$type,&$size) {
  287. switch($type) {
  288. case '0001': $type = 'UBYTE'; $size=1; break;
  289. case '0002': $type = 'ASCII'; $size=1; break;
  290. case '0003': $type = 'USHORT'; $size=2; break;
  291. case '0004': $type = 'ULONG'; $size=4; break;
  292. case '0005': $type = 'URATIONAL'; $size=8; break;
  293. case '0006': $type = 'SBYTE'; $size=1; break;
  294. case '0007': $type = 'UNDEFINED'; $size=1; break;
  295. case '0008': $type = 'SSHORT'; $size=2; break;
  296. case '0009': $type = 'SLONG'; $size=4; break;
  297. case '000a': $type = 'SRATIONAL'; $size=8; break;
  298. case '000b': $type = 'FLOAT'; $size=4; break;
  299. case '000c': $type = 'DOUBLE'; $size=8; break;
  300. default: $type = 'error:'.$type; $size=0; break;
  301. }
  302. return $type;
  303. }
  304. //================================================================================================
  305. // processes a irrational number
  306. //================================================================================================
  307. function unRational($data, $type, $intel) {
  308. $data = bin2hex($data);
  309. if ($intel == 1) {
  310. $data = intel2Moto($data);
  311. $top = hexdec(substr($data,8,8)); // intel stores them bottom-top
  312. $bottom = hexdec(substr($data,0,8)); // intel stores them bottom-top
  313. } else {
  314. $top = hexdec(substr($data,0,8)); // motorola stores them top-bottom
  315. $bottom = hexdec(substr($data,8,8)); // motorola stores them top-bottom
  316. }
  317. if ($type == 'SRATIONAL' && $top > 2147483647) $top = $top - 4294967296; // this makes the number signed instead of unsigned
  318. if ($bottom != 0)
  319. $data=$top/$bottom;
  320. else
  321. if ($top == 0)
  322. $data = 0;
  323. else
  324. $data = $top.'/'.$bottom;
  325. return $data;
  326. }
  327. //================================================================================================
  328. // processes a rational number
  329. //================================================================================================
  330. function rational($data,$type,$intel) {
  331. if (($type == 'USHORT' || $type == 'SSHORT')) {
  332. $data = substr($data,0,2);
  333. }
  334. $data = bin2hex($data);
  335. if ($intel == 1) {
  336. $data = intel2Moto($data);
  337. }
  338. $data = hexdec($data);
  339. if ($type == 'SSHORT' && $data > 32767) $data = $data - 65536; // this makes the number signed instead of unsigned
  340. if ($type == 'SLONG' && $data > 2147483647) $data = $data - 4294967296; // this makes the number signed instead of unsigned
  341. return $data;
  342. }
  343. //================================================================================================
  344. // Formats Data for the data type
  345. //================================================================================================
  346. function formatData($type,$tag,$intel,$data) {
  347. switch ($type) {
  348. case 'ASCII':
  349. if (($pos = strpos($data, chr(0))) !== false) { // Search for a null byte and stop there.
  350. $data = substr($data, 0, $pos);
  351. }
  352. if ($tag == '010f') $data = ucwords(strtolower(trim($data))); // Format certain kinds of strings nicely (Camera make etc.)
  353. break;
  354. case 'URATIONAL':
  355. case 'SRATIONAL':
  356. switch ($tag) {
  357. case '011a': // XResolution
  358. case '011b': // YResolution
  359. $data = round(unRational($data,$type,$intel)).' dots per ResolutionUnit';
  360. break;
  361. case '829a': // Exposure Time
  362. $data = formatExposure(unRational($data,$type,$intel));
  363. break;
  364. case '829d': // FNumber
  365. $data = 'f/'.unRational($data,$type,$intel);
  366. break;
  367. case '9204': // ExposureBiasValue
  368. $data = round(unRational($data,$type,$intel), 2) . ' EV';
  369. break;
  370. case '9205': // ApertureValue
  371. case '9202': // MaxApertureValue
  372. // ApertureValue is given in the APEX Mode. Many thanks to Matthieu Froment for this code
  373. // The formula is : Aperture = 2*log2(FNumber) <=> FNumber = e((Aperture.ln(2))/2)
  374. $datum = exp((unRational($data,$type,$intel)*log(2))/2);
  375. $data = round($datum, 1);// Focal is given with a precision of 1 digit.
  376. $data='f/'.$datum;
  377. break;
  378. case '920a': // FocalLength
  379. $data = unRational($data,$type,$intel).' mm';
  380. break;
  381. case '9201': // ShutterSpeedValue
  382. // The ShutterSpeedValue is given in the APEX mode. Many thanks to Matthieu Froment for this code
  383. // The formula is : Shutter = - log2(exposureTime) (Appendix C of EXIF spec.)
  384. // Where shutter is in APEX, log2(exposure) = ln(exposure)/ln(2)
  385. // So final formula is : exposure = exp(-ln(2).shutter)
  386. // The formula can be developed : exposure = 1/(exp(ln(2).shutter))
  387. $datum = exp(unRational($data,$type,$intel) * log(2));
  388. if ($datum != 0) $datum = 1/$datum;
  389. $data = formatExposure($datum);
  390. break;
  391. default:
  392. $data = unRational($data,$type,$intel);
  393. break;
  394. }
  395. break;
  396. case 'USHORT':
  397. case 'SSHORT':
  398. case 'ULONG':
  399. case 'SLONG':
  400. case 'FLOAT':
  401. case 'DOUBLE':
  402. $data = rational($data,$type,$intel);
  403. switch ($tag) {
  404. case '0112': // Orientation
  405. // Example of how all of these tag formatters should be...
  406. switch ($data) {
  407. case 0 : // not set, presume normal
  408. case 1 : $data = gettext('1: Normal (0 deg)'); break;
  409. case 2 : $data = gettext('2: Mirrored'); break;
  410. case 3 : $data = gettext('3: Upside-down'); break;
  411. case 4 : $data = gettext('4: Upside-down Mirrored'); break;
  412. case 5 : $data = gettext('5: 90 deg CW Mirrored'); break;
  413. case 6 : $data = gettext('6: 90 deg CCW'); break;
  414. case 7 : $data = gettext('7: 90 deg CCW Mirrored'); break;
  415. case 8 : $data = gettext('8: 90 deg CW'); break;
  416. default : $data = sprintf(gettext('%d: Unknown'),$data); break;
  417. }
  418. break;
  419. case '0128': // ResolutionUnit
  420. case 'a210': // FocalPlaneResolutionUnit
  421. case '0128': // ThumbnailResolutionUnit
  422. switch ($data) {
  423. case 1: $data = gettext('No Unit'); break;
  424. case 2: $data = gettext('Inch'); break;
  425. case 3: $data = gettext('Centimeter'); break;
  426. }
  427. break;
  428. case '0213': // YCbCrPositioning
  429. switch ($data) {
  430. case 1: $data = gettext('Center of Pixel Array'); break;
  431. case 2: $data = gettext('Datum Point'); break;
  432. }
  433. break;
  434. case '8822': // ExposureProgram
  435. switch ($data) {
  436. case 1: $data = gettext('Manual'); break;
  437. case 2: $data = gettext('Program'); break;
  438. case 3: $data = gettext('Aperture Priority'); break;
  439. case 4: $data = gettext('Shutter Priority'); break;
  440. case 5: $data = gettext('Program Creative'); break;
  441. case 6: $data = gettext('Program Action'); break;
  442. case 7: $data = gettext('Portrait'); break;
  443. case 8: $data = gettext('Landscape'); break;
  444. default: $data = gettext('Unknown').': '.$data; break;
  445. }
  446. break;
  447. case '9207': // MeteringMode
  448. switch ($data) {
  449. case 1: $data = gettext('Average'); break;
  450. case 2: $data = gettext('Center Weighted Average'); break;
  451. case 3: $data = gettext('Spot'); break;
  452. case 4: $data = gettext('Multi-Spot'); break;
  453. case 5: $data = gettext('Pattern'); break;
  454. case 6: $data = gettext('Partial'); break;
  455. case 255: $data = gettext('Other'); break;
  456. default: $data = gettext('Unknown').': '.$data; break;
  457. }
  458. break;
  459. case '9208': // LightSource
  460. switch ($data) {
  461. case 1: $data = gettext('Daylight'); break;
  462. case 2: $data = gettext('Fluorescent'); break;
  463. case 3: $data = gettext('Tungsten'); break; // 3 Tungsten (Incandescent light)
  464. // 4 Flash
  465. // 9 Fine Weather
  466. case 10: $data = gettext('Flash'); break; // 10 Cloudy Weather
  467. // 11 Shade
  468. // 12 Daylight Fluorescent (D 5700 - 7100K)
  469. // 13 Day White Fluorescent (N 4600 - 5400K)
  470. // 14 Cool White Fluorescent (W 3900 -4500K)
  471. // 15 White Fluorescent (WW 3200 - 3700K)
  472. // 10 Flash
  473. case 17: $data = gettext('Standard Light A'); break;
  474. case 18: $data = gettext('Standard Light B'); break;
  475. case 19: $data = gettext('Standard Light C'); break;
  476. case 20: $data = gettext('D55'); break;
  477. case 21: $data = gettext('D65'); break;
  478. case 22: $data = gettext('D75'); break;
  479. case 23: $data = gettext('D50'); break;
  480. case 24: $data = gettext('ISO Studio Tungsten'); break;
  481. case 255: $data = gettext('Other'); break;
  482. default: $data = gettext('Unknown').': '.$data; break;
  483. }
  484. break;
  485. case '9209': // Flash
  486. switch ($data) {
  487. case 0: $data = gettext('No Flash'); break;
  488. case 1: $data = gettext('Flash'); break;
  489. case 5: $data = gettext('Flash, strobe return light not detected'); break;
  490. case 7: $data = gettext('Flash, strobe return light detected'); break;
  491. case 9: $data = gettext('Compulsory Flash'); break;
  492. case 13: $data = gettext('Compulsory Flash, Return light not detected'); break;
  493. case 15: $data = gettext('Compulsory Flash, Return light detected'); break;
  494. case 16: $data = gettext('No Flash'); break;
  495. case 24: $data = gettext('No Flash'); break;
  496. case 25: $data = gettext('Flash, Auto-Mode'); break;
  497. case 29: $data = gettext('Flash, Auto-Mode, Return light not detected'); break;
  498. case 31: $data = gettext('Flash, Auto-Mode, Return light detected'); break;
  499. case 32: $data = gettext('No Flash'); break;
  500. case 65: $data = gettext('Red Eye'); break;
  501. case 69: $data = gettext('Red Eye, Return light not detected'); break;
  502. case 71: $data = gettext('Red Eye, Return light detected'); break;
  503. case 73: $data = gettext('Red Eye, Compulsory Flash'); break;
  504. case 77: $data = gettext('Red Eye, Compulsory Flash, Return light not detected'); break;
  505. case 79: $data = gettext('Red Eye, Compulsory Flash, Return light detected'); break;
  506. case 89: $data = gettext('Red Eye, Auto-Mode'); break;
  507. case 93: $data = gettext('Red Eye, Auto-Mode, Return light not detected'); break;
  508. case 95: $data = gettext('Red Eye, Auto-Mode, Return light detected'); break;
  509. default: $data = gettext('Unknown').': '.$data; break;
  510. }
  511. break;
  512. case 'a001': // ColorSpace
  513. if ($data == 1) $data = gettext('sRGB');
  514. else $data = gettext('Uncalibrated');
  515. break;
  516. case 'a002': // ExifImageWidth
  517. case 'a003': // ExifImageHeight
  518. $data = $data. ' '.gettext('pixels');
  519. break;
  520. case '0103': // Compression
  521. switch ($data) {
  522. case 1: $data = gettext('No Compression'); break;
  523. case 6: $data = gettext('Jpeg Compression'); break;
  524. default: $data = gettext('Unknown').': '.$data; break;
  525. }
  526. break;
  527. case 'a217': // SensingMethod
  528. switch ($data) {
  529. case 1: $data = gettext('Not defined'); break;
  530. case 2: $data = gettext('One Chip Color Area Sensor'); break;
  531. case 3: $data = gettext('Two Chip Color Area Sensor'); break;
  532. case 4: $data = gettext('Three Chip Color Area Sensor'); break;
  533. case 5: $data = gettext('Color Sequential Area Sensor'); break;
  534. case 7: $data = gettext('Trilinear Sensor'); break;
  535. case 8: $data = gettext('Color Sequential Linear Sensor'); break;
  536. default: $data = gettext('Unknown').': '.$data; break;
  537. }
  538. break;
  539. case '0106': // PhotometricInterpretation
  540. switch ($data) {
  541. case 1: $data = gettext('Monochrome'); break;
  542. case 2: $data = gettext('RGB'); break;
  543. case 6: $data = gettext('YCbCr'); break;
  544. default: $data = gettext('Unknown').': '.$data; break;
  545. }
  546. break;
  547. //case "a408": // Contrast
  548. //case "a40a": //Sharpness
  549. // switch($data) {
  550. // case 0: $data="Normal"; break;
  551. // case 1: $data="Soft"; break;
  552. // case 2: $data="Hard"; break;
  553. // default: $data="Unknown"; break;
  554. // }
  555. // break;
  556. //case "a409": // Saturation
  557. // switch($data) {
  558. // case 0: $data="Normal"; break;
  559. // case 1: $data="Low saturation"; break;
  560. // case 2: $data="High saturation"; break;
  561. // default: $data="Unknown"; break;
  562. // }
  563. // break;
  564. //case "a402": // Exposure Mode
  565. // switch($data) {
  566. // case 0: $data="Auto exposure"; break;
  567. // case 1: $data="Manual exposure"; break;
  568. // case 2: $data="Auto bracket"; break;
  569. // default: $data="Unknown"; break;
  570. // }
  571. // break;
  572. }
  573. break;
  574. case 'UNDEFINED':
  575. switch ($tag) {
  576. case '9000': // ExifVersion
  577. case 'a000': // FlashPixVersion
  578. case '0002': // InteroperabilityVersion
  579. $data=gettext('version').' '.$data/100;
  580. break;
  581. case 'a300': // FileSource
  582. $data = bin2hex($data);
  583. $data = str_replace('00','',$data);
  584. $data = str_replace('03',gettext('Digital Still Camera'),$data);
  585. break;
  586. case 'a301': // SceneType
  587. $data = bin2hex($data);
  588. $data = str_replace('00','',$data);
  589. $data = str_replace('01',gettext('Directly Photographed'),$data);
  590. break;
  591. case '9101': // ComponentsConfiguration
  592. $data = bin2hex($data);
  593. $data = str_replace('01','Y',$data);
  594. $data = str_replace('02','Cb',$data);
  595. $data = str_replace('03','Cr',$data);
  596. $data = str_replace('04','R',$data);
  597. $data = str_replace('05','G',$data);
  598. $data = str_replace('06','B',$data);
  599. $data = str_replace('00','',$data);
  600. break;
  601. //case "9286": //UserComment
  602. // $encoding = rtrim(substr($data, 0, 8));
  603. // $data = rtrim(substr($data, 8));
  604. // break;
  605. }
  606. break;
  607. default:
  608. $data = bin2hex($data);
  609. if ($intel == 1) $data = intel2Moto($data);
  610. break;
  611. }
  612. return $data;
  613. }
  614. function formatExposure($data) {
  615. if (strpos($data,'/')===false) {
  616. if ($data > 1) {
  617. return round($data, 2).' '.gettext('sec');
  618. } else {
  619. $n=0; $d=0;
  620. ConvertToFraction($data, $n, $d);
  621. return $n.'/'.$d.' '.gettext('sec');
  622. }
  623. } else {
  624. return gettext('Bulb');
  625. }
  626. }
  627. //================================================================================================
  628. // Reads one standard IFD entry
  629. //================================================================================================
  630. function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
  631. if (feof($in)) { // test to make sure we can still read.
  632. $result['Errors'] = $result['Errors']+1;
  633. return;
  634. }
  635. // 2 byte tag
  636. $tag = bin2hex(fread($in, 2));
  637. if ($intel == 1) $tag = intel2Moto($tag);
  638. $tag_name = lookup_tag($tag);
  639. // 2 byte datatype
  640. $type = bin2hex(fread($in, 2));
  641. if ($intel == 1) $type = intel2Moto($type);
  642. lookup_type($type, $size);
  643. if (strpos($tag_name, 'unknown:') !== false && strpos($type, 'error:') !== false) { // we have an error
  644. $result['Errors'] = $result['Errors']+1;
  645. return;
  646. }
  647. // 4 byte number of elements
  648. $count = bin2hex(fread($in, 4));
  649. if ($intel == 1) $count = intel2Moto($count);
  650. $bytesofdata = $size*hexdec($count);
  651. // 4 byte value or pointer to value if larger than 4 bytes
  652. $value = fread( $in, 4 );
  653. if ($bytesofdata <= 4) { // if datatype is 4 bytes or less, its the value
  654. $data = $value;
  655. } else if ($bytesofdata < 100000) { // otherwise its a pointer to the value, so lets go get it
  656. $value = bin2hex($value);
  657. if ($intel == 1) $value = intel2Moto($value);
  658. $v = fseek($seek, $globalOffset+hexdec($value)); // offsets are from TIFF header which is 12 bytes from the start of the file
  659. if ($v == 0) {
  660. $data = fread($seek, $bytesofdata);
  661. } else if ($v == -1) {
  662. $result['Errors'] = $result['Errors']+1;
  663. }
  664. } else { // bytesofdata was too big, so the exif had an error
  665. $result['Errors'] = $result['Errors']+1;
  666. return;
  667. }
  668. if ($tag_name == 'MakerNote') { // if its a maker tag, we need to parse this specially
  669. $make = $result['IFD0']['Make'];
  670. if ($result['VerboseOutput'] == 1) {
  671. $result[$ifd_name]['MakerNote']['RawData'] = $data;
  672. }
  673. if (preg_match('/NIKON/i',$make)) {
  674. require_once(dirname(__FILE__).'/makers/nikon.php');
  675. parseNikon($data,$result);
  676. $result[$ifd_name]['KnownMaker'] = 1;
  677. } else if (preg_match('/OLYMPUS/i',$make)) {
  678. require_once(dirname(__FILE__).'/makers/olympus.php');
  679. parseOlympus($data,$result,$seek,$globalOffset);
  680. $result[$ifd_name]['KnownMaker'] = 1;
  681. } else if (preg_match('/Canon/i',$make)) {
  682. require_once(dirname(__FILE__).'/makers/canon.php');
  683. parseCanon($data,$result,$seek,$globalOffset);
  684. $result[$ifd_name]['KnownMaker'] = 1;
  685. } else if (preg_match('/FUJIFILM/i',$make)) {
  686. require_once(dirname(__FILE__).'/makers/fujifilm.php');
  687. parseFujifilm($data,$result);
  688. $result[$ifd_name]['KnownMaker'] = 1;
  689. } else if (preg_match('/SANYO/i',$make)) {
  690. require_once(dirname(__FILE__).'/makers/sanyo.php');
  691. parseSanyo($data,$result,$seek,$globalOffset);
  692. $result[$ifd_name]['KnownMaker'] = 1;
  693. } else if (preg_match('/Panasonic/i',$make)) {
  694. require_once(dirname(__FILE__).'/makers/panasonic.php');
  695. parsePanasonic($data,$result,$seek,$globalOffset);
  696. $result[$ifd_name]['KnownMaker'] = 1;
  697. } else {
  698. $result[$ifd_name]['KnownMaker'] = 0;
  699. }
  700. } else if ($tag_name == 'GPSInfoOffset') {
  701. require_once(dirname(__FILE__).'/makers/gps.php');
  702. $formated_data = formatData($type,$tag,$intel,$data);
  703. $result[$ifd_name]['GPSInfo'] = $formated_data;
  704. parseGPS($data,$result,$formated_data,$seek,$globalOffset);
  705. } else {
  706. // Format the data depending on the type and tag
  707. $formated_data = formatData($type,$tag,$intel,$data);
  708. $result[$ifd_name][$tag_name] = $formated_data;
  709. if ($result['VerboseOutput'] == 1) {
  710. if ($type == 'URATIONAL' || $type == 'SRATIONAL' || $type == 'USHORT' || $type == 'SSHORT' || $type == 'ULONG' || $type == 'SLONG' || $type == 'FLOAT' || $type == 'DOUBLE') {
  711. $data = bin2hex($data);
  712. if ($intel == 1) $data = intel2Moto($data);
  713. }
  714. $result[$ifd_name][$tag_name.'_Verbose']['RawData'] = $data;
  715. $result[$ifd_name][$tag_name.'_Verbose']['Type'] = $type;
  716. $result[$ifd_name][$tag_name.'_Verbose']['Bytes'] = $bytesofdata;
  717. }
  718. }
  719. }
  720. //================================================================================================
  721. // Pass in a file and this reads the EXIF data
  722. //
  723. // Usefull resources
  724. // http:// www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
  725. // http:// www.w3.org/Graphics/JPEG/jfif.txt
  726. // http:// exif.org/
  727. // http:// www.ozhiker.com/electronics/pjmt/library/list_contents.php4
  728. // http:// www.ozhiker.com/electronics/pjmt/jpeg_info/makernotes.html
  729. // http:// pel.sourceforge.net/
  730. // http:// us2.php.net/manual/en/function.exif-read-data.php
  731. //================================================================================================
  732. function read_exif_data_raw($path,$verbose) {
  733. if ($path == '' || $path == 'none') return;
  734. $in = @fopen($path, 'rb'); // the b is for windows machines to open in binary mode
  735. $seek = @fopen($path, 'rb'); // There may be an elegant way to do this with one file handle.
  736. $globalOffset = 0;
  737. if (!isset($verbose)) $verbose=0;
  738. $result['VerboseOutput'] = $verbose;
  739. $result['Errors'] = 0;
  740. if (!$in || !$seek) { // if the path was invalid, this error will catch it
  741. $result['Errors'] = 1;
  742. $result['Error'][$result['Errors']] = gettext('The file could not be found.');
  743. return $result;
  744. }
  745. $GLOBALS['exiferFileSize'] = filesize($path);
  746. // First 2 bytes of JPEG are 0xFFD8
  747. $data = bin2hex(fread( $in, 2 ));
  748. if ($data == 'ffd8') {
  749. $result['ValidJpeg'] = 1;
  750. } else {
  751. $result['ValidJpeg'] = 0;
  752. fseek($in, 0);
  753. }
  754. $result['ValidIPTCData'] = 0;
  755. $result['ValidJFIFData'] = 0;
  756. $result['ValidEXIFData'] = 0;
  757. $result['ValidAPP2Data'] = 0;
  758. $result['ValidCOMData'] = 0;
  759. if ($result['ValidJpeg'] == 1) {
  760. // Next 2 bytes are MARKER tag (0xFFE#)
  761. $data = bin2hex(fread( $in, 2 ));
  762. $size = bin2hex(fread( $in, 2 ));
  763. // LOOP THROUGH MARKERS TILL YOU GET TO FFE1 (exif marker)
  764. $abortCount = 0;
  765. while(!feof($in) && $data!='ffe1' && $data!='ffc0' && $data!='ffd9' && ++$abortCount < 200) {
  766. if ($data == 'ffe0') { // JFIF Marker
  767. $result['ValidJFIFData'] = 1;
  768. $result['JFIF']['Size'] = hexdec($size);
  769. if (hexdec($size)-2 > 0) {
  770. $data = fread( $in, hexdec($size)-2);
  771. $result['JFIF']['Data'] = $data;
  772. }
  773. $result['JFIF']['Identifier'] = substr($data,0,5);;
  774. $result['JFIF']['ExtensionCode'] = bin2hex(substr($data,6,1));
  775. $globalOffset+=hexdec($size)+2;
  776. } else if ($data == 'ffed') { // IPTC Marker
  777. $result['ValidIPTCData'] = 1;
  778. $result['IPTC']['Size'] = hexdec($size);
  779. if (hexdec($size)-2 > 0) {
  780. $data = fread( $in, hexdec($size)-2);
  781. $result['IPTC']['Data'] = $data ;
  782. }
  783. $globalOffset+=hexdec($size)+2;
  784. } else if ($data == 'ffe2') { // EXIF extension Marker
  785. $result['ValidAPP2Data'] = 1;
  786. $result['APP2']['Size'] = hexdec($size);
  787. if (hexdec($size)-2 > 0) {
  788. $data = fread( $in, hexdec($size)-2);
  789. $result['APP2']['Data'] = $data ;
  790. }
  791. $globalOffset+=hexdec($size)+2;
  792. } else if ($data == 'fffe') { // COM extension Marker
  793. $result['ValidCOMData'] = 1;
  794. $result['COM']['Size'] = hexdec($size);
  795. if (hexdec($size)-2 > 0) {
  796. $data = fread( $in, hexdec($size)-2);
  797. $result['COM']['Data'] = $data ;
  798. }
  799. $globalOffset+=hexdec($size)+2;
  800. } else if ($data == 'ffe1') {
  801. $result['ValidEXIFData'] = 1;
  802. }
  803. $data = bin2hex(fread( $in, 2 ));
  804. $size = bin2hex(fread( $in, 2 ));
  805. }
  806. // END MARKER LOOP
  807. if ($data == 'ffe1') {
  808. $result['ValidEXIFData'] = 1;
  809. } else {
  810. fclose($in);
  811. fclose($seek);
  812. return $result;
  813. }
  814. // Size of APP1
  815. $result['APP1Size'] = hexdec($size);
  816. // Start of APP1 block starts with 'Exif' header (6 bytes)
  817. $header = fread( $in, 6 );
  818. } // END IF ValidJpeg
  819. // Then theres a TIFF header with 2 bytes of endieness (II or MM)
  820. $header = fread( $in, 2 );
  821. if ($header==='II') {
  822. $intel=1;
  823. $result['Endien'] = 'Intel';
  824. } else if ($header==='MM') {
  825. $intel=0;
  826. $result['Endien'] = 'Motorola';
  827. } else {
  828. $intel=1; // not sure what the default should be, but this seems reasonable
  829. $result['Endien'] = 'Unknown';
  830. }
  831. // 2 bytes of 0x002a
  832. $tag = bin2hex(fread( $in, 2 ));
  833. // Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header)
  834. $offset = bin2hex(fread( $in, 4 ));
  835. if ($intel == 1) $offset = intel2Moto($offset);
  836. // Check for extremely large values here
  837. if (hexdec($offset) > 100000) {
  838. $result['ValidEXIFData'] = 0;
  839. fclose($in);
  840. fclose($seek);
  841. return $result;
  842. }
  843. if (hexdec($offset)>8) $unknown = fread( $in, hexdec($offset)-8); // fixed this bug in 1.3
  844. // add 12 to the offset to account for TIFF header
  845. if ($result['ValidJpeg'] == 1) {
  846. $globalOffset+=12;
  847. }
  848. //===========================================================
  849. // Start of IFD0
  850. $num = bin2hex(fread( $in, 2 ));
  851. if ($intel == 1) $num = intel2Moto($num);
  852. $num = hexdec($num);
  853. $result['IFD0NumTags'] = $num;
  854. if ($num<1000) { // 1000 entries is too much and is probably an error.
  855. for($i=0; $i<$num; $i++) {
  856. read_entry($result,$in,$seek,$intel,'IFD0',$globalOffset);
  857. }
  858. } else {
  859. $result['Errors'] = $result['Errors']+1;
  860. $result['Error'][$result['Errors']] = 'Illegal size for IFD0';
  861. }
  862. // store offset to IFD1
  863. $offset = bin2hex(fread( $in, 4 ));
  864. if ($intel == 1) $offset = intel2Moto($offset);
  865. $result['IFD1Offset'] = hexdec($offset);
  866. // Check for SubIFD
  867. if (!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset'] == 0) {
  868. fclose($in);
  869. fclose($seek);
  870. return $result;
  871. }
  872. // seek to SubIFD (Value of ExifOffset tag) above.
  873. $ExitOffset = $result['IFD0']['ExifOffset'];
  874. $v = fseek($in,$globalOffset+$ExitOffset);
  875. if ($v == -1) {
  876. $result['Errors'] = $result['Errors']+1;
  877. $result['Error'][$result['Errors']] = gettext('Could not Find SubIFD');
  878. }
  879. //===========================================================
  880. // Start of SubIFD
  881. $num = bin2hex(fread( $in, 2 ));
  882. if ($intel == 1) $num = intel2Moto($num);
  883. $num = hexdec($num);
  884. $result['SubIFDNumTags'] = $num;
  885. if ($num<1000) { // 1000 entries is too much and is probably an error.
  886. for($i=0; $i<$num; $i++) {
  887. read_entry($result,$in,$seek,$intel,'SubIFD',$globalOffset);
  888. }
  889. } else {
  890. $result['Errors'] = $result['Errors']+1;
  891. $result['Error'][$result['Errors']] = gettext('Illegal size for SubIFD');
  892. }
  893. // Add the 35mm equivalent focal length:
  894. if (isset($result['IFD0']['FocalLengthIn35mmFilm']) && !isset($result['SubIFD']['FocalLengthIn35mmFilm'])) { // found in the wrong place
  895. $result['SubIFD']['FocalLengthIn35mmFilm'] = $result['IFD0']['FocalLengthIn35mmFilm'];
  896. }
  897. if (!isset($result['SubIFD']['FocalLengthIn35mmFilm'])) {
  898. $result['SubIFD']['FocalLengthIn35mmFilm'] = get35mmEquivFocalLength($result);
  899. }
  900. // Check for IFD1
  901. if (!isset($result['IFD1Offset']) || $result['IFD1Offset'] == 0) {
  902. fclose($in);
  903. fclose($seek);
  904. return $result;
  905. }
  906. // seek to IFD1
  907. $v = fseek($in,$globalOffset+$result['IFD1Offset']);
  908. if ($v == -1) {
  909. $result['Errors'] = $result['Errors']+1;
  910. $result['Error'][$result['Errors']] = gettext('Could not Find IFD1');
  911. }
  912. //===========================================================
  913. // Start of IFD1
  914. $num = bin2hex(fread( $in, 2 ));
  915. if ($intel == 1) $num = intel2Moto($num);
  916. $num = hexdec($num);
  917. $result['IFD1NumTags'] = $num;
  918. if ($num<1000) { // 1000 entries is too much and is probably an error.
  919. for($i=0; $i<$num; $i++) {
  920. read_entry($result,$in,$seek,$intel,'IFD1',$globalOffset);
  921. }
  922. } else {
  923. $result['Errors'] = $result['Errors']+1;
  924. $result['Error'][$result['Errors']] = gettext('Illegal size for IFD1');
  925. }
  926. // If verbose output is on, include the thumbnail raw data...
  927. if ($result['VerboseOutput'] == 1 && $result['IFD1']['JpegIFOffset']>0 && $result['IFD1']['JpegIFByteCount']>0) {
  928. $v = fseek($seek,$globalOffset+$result['IFD1']['JpegIFOffset']);
  929. if ($v == 0) {
  930. $data = fread($seek, $result['IFD1']['JpegIFByteCount']);
  931. } else if ($v == -1) {
  932. $result['Errors'] = $result['Errors']+1;
  933. }
  934. $result['IFD1']['ThumbnailData'] = $data;
  935. }
  936. // Check for Interoperability IFD
  937. if (!isset($result['SubIFD']['ExifInteroperabilityOffset']) || $result['SubIFD']['ExifInteroperabilityOffset'] == 0) {
  938. fclose($in);
  939. fclose($seek);
  940. return $result;
  941. }
  942. // Seek to InteroperabilityIFD
  943. $v = fseek($in,$globalOffset+$result['SubIFD']['ExifInteroperabilityOffset']);
  944. if ($v == -1) {
  945. $result['Errors'] = $result['Errors']+1;
  946. $result['Error'][$result['Errors']] = gettext('Could not Find InteroperabilityIFD');
  947. }
  948. //===========================================================
  949. // Start of InteroperabilityIFD
  950. $num = bin2hex(fread( $in, 2 ));
  951. if ($intel == 1) $num = intel2Moto($num);
  952. $num = hexdec($num);
  953. $result['InteroperabilityIFDNumTags'] = $num;
  954. if ($num<1000) { // 1000 entries is too much and is probably an error.
  955. for($i=0; $i<$num; $i++) {
  956. read_entry($result,$in,$seek,$intel,'InteroperabilityIFD',$globalOffset);
  957. }
  958. } else {
  959. $result['Errors'] = $result['Errors']+1;
  960. $result['Error'][$result['Errors']] = gettext('Illegal size for InteroperabilityIFD');
  961. }
  962. fclose($in);
  963. fclose($seek);
  964. return $result;
  965. }
  966. //=========================================================
  967. // Converts a floating point number into a simple fraction.
  968. //=========================================================
  969. function ConvertToFraction($v, &$n, &$d) {
  970. if ($v == 0) {
  971. $n = 0;
  972. $d = 1;
  973. return;
  974. }
  975. for ($n=1; $n<100; $n++) {
  976. $v1 = 1/$v*$n;
  977. $d = round($v1, 0);
  978. if (abs($d - $v1) < 0.02) return; // within tolarance
  979. }
  980. }
  981. //================================================================================================
  982. // Calculates the 35mm-equivalent focal length from the reported sensor resolution, by Tristan Harward.
  983. //================================================================================================
  984. function get35mmEquivFocalLength(&$result) {
  985. if (isset($result['SubIFD']['ExifImageWidth'])) {
  986. $width = $result['SubIFD']['ExifImageWidth'];
  987. } else {
  988. $width = 0;
  989. }
  990. if (isset($result['SubIFD']['FocalPlaneResolutionUnit'])) {
  991. $units = $result['SubIFD']['FocalPlaneResolutionUnit'];
  992. } else {
  993. $units = '';
  994. }
  995. $unitfactor = 1;
  996. switch ($units) {
  997. case 'Inch' : $unitfactor = 25.4; break;
  998. case 'Centimeter' : $unitfactor = 10; break;
  999. case 'No Unit' : $unitfactor = 25.4; break;
  1000. default : $unitfactor = 25.4;
  1001. }
  1002. if (isset($result['SubIFD']['FocalPlaneXResolution'])) {
  1003. $xres = $result['SubIFD']['FocalPlaneXResolution'];
  1004. } else {
  1005. $xres = '';
  1006. }
  1007. if (isset($result['SubIFD']['FocalLength'])) {
  1008. $fl = $result['SubIFD']['FocalLength'];
  1009. } else {
  1010. $fl = 0;
  1011. }
  1012. if (($width != 0) && !empty($units) && !empty($xres) && !empty($fl) && !empty($width)) {
  1013. $ccdwidth = ($width * $unitfactor) / $xres;
  1014. $equivfl = $fl / $ccdwidth*36+0.5;
  1015. return $equivfl;
  1016. }
  1017. return null;
  1018. }
  1019. ?>