PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/exifer1_5/exif.php

https://github.com/aravindc/pixelpost
PHP | 994 lines | 658 code | 91 blank | 245 comment | 383 complexity | 31c92afb04bc9426a4dfeba54e65a7df MD5 | raw file
Possible License(s): GPL-2.0
  1. <?php
  2. //SVN file version:
  3. //$Id$
  4. //================================================================================================
  5. //================================================================================================
  6. //================================================================================================
  7. /*
  8. Exifer 1.5
  9. Extracts EXIF information from digital photos.
  10. Copyright Š 2005 Jake Olefsky
  11. http://www.offsky.com/software/exif/index.php
  12. jake@olefsky.com
  13. This program is free software; you can redistribute it and/or modify it under the terms of
  14. the GNU General Public License as published by the Free Software Foundation; either version 2
  15. of the License, or (at your option) any later version.
  16. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  17. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  18. See the GNU General Public License for more details. http://www.gnu.org/copyleft/gpl.html
  19. SUMMARY:
  20. This script will correctly parse all of the EXIF data included in images taken
  21. with digital cameras. It will read the IDF0, IDF1, SubIDF and InteroperabilityIFD
  22. fields as well as parsing some of the MakerNote fields that vary depending on
  23. camera make and model. This script parses more tags than the internal PHP exif
  24. implementation and it will correctly identify and decode what all the values mean.
  25. This version will correctly parse the MakerNote field for Nikon, Olympus, and Canon
  26. digital cameras. Others will follow.
  27. TESTED WITH:
  28. Nikon CoolPix 700
  29. Nikon CoolPix 4500
  30. Nikon CoolPix 950
  31. Nikon Coolpix 5700
  32. Canon PowerShot S200
  33. Canon PowerShot S110
  34. Olympus C2040Z
  35. Olympus C960
  36. Canon Ixus
  37. Canon EOS 300D
  38. Canon Digital Rebel
  39. Canon EOS 10D
  40. Canon PowerShot G2
  41. FujiFilm DX 10
  42. FujiFilm MX-1200
  43. FujiFilm FinePix2400
  44. FujiFilm FinePix2600
  45. FujiFilm FinePix S602
  46. FujiFilm FinePix40i
  47. Sony D700
  48. Sony Cybershot
  49. Kodak DC210
  50. Kodak DC240
  51. Kodak DC4800
  52. Kodak DX3215
  53. Ricoh RDC-5300
  54. Sanyo VPC-G250
  55. Sanyo VPC-SX550
  56. Epson 3100z
  57. If you improve this script or test it with a camera not on my list, please email me a copy of
  58. the improvement so everyone can benefit from it. Thanks!
  59. VERSION HISTORY:
  60. 1.0 Sept 23, 2002
  61. First Public Release
  62. 1.1 Jan 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 Mar 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 Jun 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 Sept 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 Feb 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. */
  110. //================================================================================================
  111. //================================================================================================
  112. //================================================================================================
  113. //================================================================================================
  114. //================================================================================================
  115. // Converts from Intel to Motorola endien. Just reverses the bytes (assumes hex is passed in)
  116. //================================================================================================
  117. //================================================================================================
  118. function intel2Moto($intel) {
  119. $len = strlen($intel);
  120. $moto="";
  121. for($i=0; $i<=$len; $i+=2) {
  122. $moto.=substr($intel,$len-$i,2);
  123. }
  124. return $moto;
  125. }
  126. //================================================================================================
  127. //================================================================================================
  128. // Looks up the name of the tag
  129. //================================================================================================
  130. //================================================================================================
  131. function lookup_tag($tag) {
  132. switch($tag) {
  133. //used by IFD0 "Camera Tags"
  134. case "000b": $tag = "ACDComment";break; //text string up to 999 bytes long
  135. case "00fe": $tag = "ImageType";break; //integer -2147483648 to 2147483647
  136. case "0106": $tag = "PhotometricInterpret";break; //?? Please send sample image with this tag
  137. case "010e": $tag = "ImageDescription";break; //text string up to 999 bytes long
  138. case "010f": $tag = "Make";break; //text string up to 999 bytes long
  139. case "0110": $tag = "Model";break; //text string up to 999 bytes long
  140. case "0112": $tag = "Orientation";break; //integer values 1-9
  141. case "0115": $tag = "SamplePerPixel";break; //integer 0-65535
  142. case "011a": $tag = "xResolution";break; //positive rational number
  143. case "011b": $tag = "yResolution";break; //positive rational number
  144. case "011c": $tag = "PlanarConfig";break; //integer values 1-2
  145. case "0128": $tag = "ResolutionUnit";break; //integer values 1-3
  146. case "0131": $tag = "Software";break; //text string up to 999 bytes long
  147. case "0132": $tag = "DateTime";break; //YYYY:MM:DD HH:MM:SS
  148. case "013b": $tag = "Artist";break; //text string up to 999 bytes long
  149. case "013c": $tag = "HostComputer";break; //text string
  150. case "013e": $tag = "WhitePoint";break; //two positive rational numbers
  151. case "013f": $tag = "PrimaryChromaticities";break; //six positive rational numbers
  152. case "0211": $tag = "YCbCrCoefficients";break; //three positive rational numbers
  153. case "0213": $tag = "YCbCrPositioning";break; //integer values 1-2
  154. case "0214": $tag = "ReferenceBlackWhite";break; //six positive rational numbers
  155. case "8298": $tag = "Copyright";break; //text string up to 999 bytes long
  156. case "8649": $tag = "PhotoshopSettings";break; //??
  157. case "8825": $tag = "GPSInfoOffset";break;
  158. case "9286": $tag = "UserCommentOld";break; //??
  159. case "8769": $tag = "ExifOffset";break; //positive integer
  160. //used by Exif SubIFD "Image Tags"
  161. case "829a": $tag = "ExposureTime";break; //seconds or fraction of seconds 1/x
  162. case "829d": $tag = "FNumber";break; //positive rational number
  163. case "8822": $tag = "ExposureProgram";break; //integer value 1-9
  164. case "8824": $tag = "SpectralSensitivity";break; //??
  165. case "8827": $tag = "ISOSpeedRatings";break; //integer 0-65535
  166. case "9000": $tag = "ExifVersion";break; //??
  167. case "9003": $tag = "DateTimeOriginal";break; //YYYY:MM:DD HH:MM:SS
  168. case "9004": $tag = "DateTimedigitized";break; //YYYY:MM:DD HH:MM:SS
  169. case "9101": $tag = "ComponentsConfiguration";break; //??
  170. case "9102": $tag = "CompressedBitsPerPixel";break; //positive rational number
  171. case "9201": $tag = "ShutterSpeedValue";break; //seconds or fraction of seconds 1/x
  172. case "9202": $tag = "ApertureValue";break; //positive rational number
  173. case "9203": $tag = "BrightnessValue";break; //positive rational number
  174. case "9204": $tag = "ExposureBiasValue";break; //positive rational number (EV)
  175. case "9205": $tag = "MaxApertureValue";break; //positive rational number
  176. case "9206": $tag = "SubjectDistance";break; //positive rational number (meters)
  177. case "9207": $tag = "MeteringMode";break; //integer 1-6 and 255
  178. case "9208": $tag = "LightSource";break; //integer 1-255
  179. case "9209": $tag = "Flash";break; //integer 1-255
  180. case "920a": $tag = "FocalLength";break; //positive rational number (mm)
  181. case "9213": $tag = "ImageHistory";break; //text string up to 999 bytes long
  182. case "927c": $tag = "MakerNote";break; //a bunch of data
  183. case "9286": $tag = "UserComment";break; //text string
  184. case "9290": $tag = "SubsecTime";break; //text string up to 999 bytes long
  185. case "9291": $tag = "SubsecTimeOriginal";break; //text string up to 999 bytes long
  186. case "9292": $tag = "SubsecTimeDigitized";break; //text string up to 999 bytes long
  187. case "a000": $tag = "FlashPixVersion";break; //??
  188. case "a001": $tag = "ColorSpace";break; //values 1 or 65535
  189. case "a002": $tag = "ExifImageWidth";break; //ingeter 1-65535
  190. case "a003": $tag = "ExifImageHeight";break; //ingeter 1-65535
  191. case "a004": $tag = "RelatedSoundFile";break; //text string 12 bytes long
  192. case "a005": $tag = "ExifInteroperabilityOffset";break; //positive integer
  193. case "a20c": $tag = "SpacialFreqResponse";break; //??
  194. case "a20b": $tag = "FlashEnergy";break; //positive rational number
  195. case "a20e": $tag = "FocalPlaneXResolution";break; //positive rational number
  196. case "a20f": $tag = "FocalPlaneYResolution";break; //positive rational number
  197. case "a210": $tag = "FocalPlaneResolutionUnit";break; //values 1-3
  198. case "a214": $tag = "SubjectLocation";break; //two integers 0-65535
  199. case "a215": $tag = "ExposureIndex";break; //positive rational number
  200. case "a217": $tag = "SensingMethod";break; //values 1-8
  201. case "a300": $tag = "FileSource";break; //integer
  202. case "a301": $tag = "SceneType";break; //integer
  203. case "a302": $tag = "CFAPattern";break; //undefined data type
  204. case "a401": $tag = "CustomerRender";break; //values 0 or 1
  205. case "a402": $tag = "ExposureMode";break; //values 0-2
  206. case "a403": $tag = "WhiteBalance";break; //values 0 or 1
  207. case "a404": $tag = "DigitalZoomRatio";break; //positive rational number
  208. case "a405": $tag = "FocalLengthIn35mmFilm";break; //??
  209. case "a406": $tag = "SceneCaptureMode";break; //values 0-3
  210. case "a407": $tag = "GainControl";break; //values 0-4
  211. case "a408": $tag = "Contrast";break; //values 0-2
  212. case "a409": $tag = "Saturation";break; //values 0-2
  213. case "a40a": $tag = "Sharpness";break; //values 0-2
  214. //used by Interoperability IFD
  215. case "0001": $tag = "InteroperabilityIndex";break; //text string 3 bytes long
  216. case "0002": $tag = "InteroperabilityVersion";break; //datatype undefined
  217. case "1000": $tag = "RelatedImageFileFormat";break; //text string up to 999 bytes long
  218. case "1001": $tag = "RelatedImageWidth";break; //integer in range 0-65535
  219. case "1002": $tag = "RelatedImageLength";break; //integer in range 0-65535
  220. //used by IFD1 "Thumbnail"
  221. case "0100": $tag = "ImageWidth";break; //integer in range 0-65535
  222. case "0101": $tag = "ImageLength";break; //integer in range 0-65535
  223. case "0102": $tag = "BitsPerSample";break; //integers in range 0-65535
  224. case "0103": $tag = "Compression";break; //values 1 or 6
  225. case "0106": $tag = "PhotometricInterpretation";break; //values 0-4
  226. case "010e": $tag = "ThumbnailDescription";break; //text string up to 999 bytes long
  227. case "010f": $tag = "ThumbnailMake";break; //text string up to 999 bytes long
  228. case "0110": $tag = "ThumbnailModel";break; //text string up to 999 bytes long
  229. case "0111": $tag = "StripOffsets";break; //??
  230. case "0112": $tag = "ThumbnailOrientation";break; //integer 1-9
  231. case "0115": $tag = "SamplesPerPixel";break; //??
  232. case "0116": $tag = "RowsPerStrip";break; //??
  233. case "0117": $tag = "StripByteCounts";break; //??
  234. case "011a": $tag = "ThumbnailXResolution";break; //positive rational number
  235. case "011b": $tag = "ThumbnailYResolution";break; //positive rational number
  236. case "011c": $tag = "PlanarConfiguration";break; //values 1 or 2
  237. case "0128": $tag = "ThumbnailResolutionUnit";break; //values 1-3
  238. case "0201": $tag = "JpegIFOffset";break;
  239. case "0202": $tag = "JpegIFByteCount";break;
  240. case "0212": $tag = "YCbCrSubSampling";break;
  241. //misc
  242. case "00ff": $tag = "SubfileType";break;
  243. case "012d": $tag = "TransferFunction";break;
  244. case "013d": $tag = "Predictor";break;
  245. case "0142": $tag = "TileWidth";break;
  246. case "0143": $tag = "TileLength";break;
  247. case "0144": $tag = "TileOffsets";break;
  248. case "0145": $tag = "TileByteCounts";break;
  249. case "014a": $tag = "SubIFDs";break;
  250. case "015b": $tag = "JPEGTables";break;
  251. case "828d": $tag = "CFARepeatPatternDim";break;
  252. case "828e": $tag = "CFAPattern";break;
  253. case "828f": $tag = "BatteryLevel";break;
  254. case "83bb": $tag = "IPTC/NAA";break;
  255. case "8773": $tag = "InterColorProfile";break;
  256. case "8828": $tag = "OECF";break;
  257. case "8829": $tag = "Interlace";break;
  258. case "882a": $tag = "TimeZoneOffset";break;
  259. case "882b": $tag = "SelfTimerMode";break;
  260. case "920b": $tag = "FlashEnergy";break;
  261. case "920c": $tag = "SpatialFrequencyResponse";break;
  262. case "920d": $tag = "Noise";break;
  263. case "9211": $tag = "ImageNumber";break;
  264. case "9212": $tag = "SecurityClassification";break;
  265. case "9214": $tag = "SubjectLocation";break;
  266. case "9215": $tag = "ExposureIndex";break;
  267. case "9216": $tag = "TIFF/EPStandardID";break;
  268. case "a20b": $tag = "FlashEnergy";break;
  269. //default: $tag = "unknown:".$tag;break;
  270. default: $tag = "unknown";break;
  271. }
  272. return $tag;
  273. }
  274. //================================================================================================
  275. //================================================================================================
  276. // Looks up the datatype
  277. //================================================================================================
  278. //================================================================================================
  279. function lookup_type(&$type,&$size) {
  280. switch($type) {
  281. case "0001": $type = "UBYTE";$size=1;break;
  282. case "0002": $type = "ASCII";$size=1;break;
  283. case "0003": $type = "USHORT";$size=2;break;
  284. case "0004": $type = "ULONG";$size=4;break;
  285. case "0005": $type = "URATIONAL";$size=8;break;
  286. case "0006": $type = "SBYTE";$size=1;break;
  287. case "0007": $type = "UNDEFINED";$size=1;break;
  288. case "0008": $type = "SSHORT";$size=2;break;
  289. case "0009": $type = "SLONG";$size=4;break;
  290. case "000a": $type = "SRATIONAL";$size=8;break;
  291. case "000b": $type = "FLOAT";$size=4;break;
  292. case "000c": $type = "DOUBLE";$size=8;break;
  293. default: $type = "error:".$type;$size=0;break;
  294. }
  295. return $type;
  296. }
  297. //================================================================================================
  298. //================================================================================================
  299. // Formats Data for the data type
  300. //================================================================================================
  301. //================================================================================================
  302. function formatData($type,$tag,$intel,$data) {
  303. if($type=="ASCII") {
  304. //do nothing
  305. } else if($type=="URATIONAL" || $type=="SRATIONAL") {
  306. $data = bin2hex($data);
  307. if($intel==1) $data = intel2Moto($data);
  308. if($intel==1) $top = hexdec(substr($data,8,8)); //intel stores them bottom-top
  309. else $top = hexdec(substr($data,0,8)); //motorola stores them top-bottom
  310. if($intel==1) $bottom = hexdec(substr($data,0,8)); //intel stores them bottom-top
  311. else $bottom = hexdec(substr($data,8,8)); //motorola stores them top-bottom
  312. if($type=="SRATIONAL" && $top>2147483647) $top = $top - 4294967296; //this makes the number signed instead of unsigned
  313. if($bottom!=0) $data=$top/$bottom;
  314. else if($top==0) $data = 0;
  315. else $data=$top."/".$bottom;
  316. if(($tag=="011a" || $tag=="011b") && $bottom==1) { //XResolution YResolution
  317. $data=$top." dots per ResolutionUnit";
  318. } else if($tag=="829a") { //Exposure Time
  319. if($top/10 == 1) $data="1/".round($bottom/10, 0)." sec";
  320. else $data=$top."/".$bottom." sec";
  321. } else if($tag=="829d") { //FNumber
  322. $data="f ".$data;
  323. } else if($tag=="9204") { //ExposureBiasValue
  324. $data=$data." EV";
  325. } else if($tag=="9205" || $tag=="9202") { //ApertureValue and MaxApertureValue
  326. //ApertureValue is given in the APEX Mode. Many thanks to Matthieu Froment for this code
  327. //The formula is : Aperture = 2*log2(FNumber) <=> FNumber = e((Aperture.ln(2))/2)
  328. $data = exp(($data*log(2))/2);
  329. $data = round($data, 1);//Focal is given with a precision of 1 digit.
  330. $data="f ".$data;
  331. } else if($tag=="920a") { //FocalLength
  332. $data=$data." mm";
  333. } else if($tag=="9201") { //ShutterSpeedValue
  334. // The ShutterSpeedValue is given in the APEX mode. Many thanks to Matthieu Froment for this code
  335. // The formula is : Shutter = - log2(exposureTime) (Appendix C of EXIF spec.)
  336. // Where shutter is in APEX, log2(exposure) = ln(exposure)/ln(2)
  337. // So final formula is : exposure = exp(-ln(2).shutter)
  338. // The formula can be developed : exposure = 1/(exp(ln(2).shutter))
  339. if($tag=='9201') {
  340. $data = exp($data * log(2));
  341. if ($data >= 1000000) {
  342. $data = null; // Throw out near-zero value
  343. } else if ($data >= 1.99) {
  344. $prec = $data >= 38 ? -1 : 0;
  345. $data = '1/' . round($data, $prec) . ' sec';
  346. } else if ($data > 0) $data = 1/$data;
  347. }
  348. else if($tag=='829a' && $top > 0 && $bottom % $top == 0) {
  349. $data = '1/' . intval($bottom/$top) . ' sec';
  350. }
  351. if (is_float($data)) {
  352. if ($data > 0) {
  353. $prec = $data < 1 ? 2 : ($data < 10 ? 1 : 0);
  354. $data = round($data, $prec) . ' sec';
  355. } else {
  356. $data = "Bulb";
  357. }
  358. }
  359. }
  360. } else if($type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
  361. $data = bin2hex($data);
  362. if($type=="USHORT" || $type=="SSHORT") $data = substr($data,0,4);
  363. if($intel==1) $data = intel2Moto($data);
  364. $data=hexdec($data);
  365. // fix for iso value (thanks to hdt from the pixelpost forum)
  366. if ($tag == "8827") $data=65535&$data; //mask 2 bytes for ISO rating
  367. if ($tag == "9209") $data=65535&$data; //mask 2 bytes for Flash info
  368. if($type=="SSHORT" && $data>32767) $data = $data - 65536; //this makes the number signed instead of unsigned
  369. if($type=="SLONG" && $data>2147483647) $data = $data - 4294967296; //this makes the number signed instead of unsigned
  370. if($tag=="0112") { //Orientation
  371. if($data==1) $data = "Normal (O deg)";
  372. else if($data==2) $data = "Mirrored";
  373. else if($data==3) $data = "Upsidedown";
  374. else if($data==4) $data = "Upsidedown Mirrored";
  375. else if($data==5) $data = "90 deg CW Mirrored";
  376. else if($data==6) $data = "90 deg CCW";
  377. else if($data==7) $data = "90 deg CCW Mirrored";
  378. else if($data==8) $data = "90 deg CW";
  379. } else if($tag=="0128" || $tag=="a210" || $tag=="0128") { //ResolutionUnit and FocalPlaneResolutionUnit and ThumbnailResolutionUnit
  380. if($data==1) $data = "No Unit";
  381. else if($data==2) $data = "Inch";
  382. else if($data==3) $data = "Centimeter";
  383. } else if($tag=="0213") { //YCbCrPositioning
  384. if($data==1) $data = "Center of Pixel Array";
  385. else if($data==2) $data = "Datum Point";
  386. } else if($tag=="8822") { //ExposureProgram
  387. if($data==1) $data = "Manual";
  388. else if($data==2) $data = "Program";
  389. else if($data==3) $data = "Aperture Priority";
  390. else if($data==4) $data = "Shutter Priority";
  391. else if($data==5) $data = "Program Creative";
  392. else if($data==6) $data = "Program Action";
  393. else if($data==7) $data = "Portrait";
  394. else if($data==8) $data = "Landscape";
  395. else $data = "Unknown: ".$data;
  396. } else if($tag=="9207") { //MeteringMode
  397. if($data==0) $data = "Unknown";
  398. else if($data==1) $data = "Average";
  399. else if($data==2) $data = "Center Weighted Average";
  400. else if($data==3) $data = "Spot";
  401. else if($data==4) $data = "Multi-Spot";
  402. else if($data==5) $data = "Multi-Segment";
  403. else if($data==6) $data = "Partial";
  404. else if($data==255) $data = "Other";
  405. //else $data = "Unknown: ".$data;
  406. else $data = "Unknown";
  407. } else if($tag=="9208") { //LightSource
  408. if($data==0) $data = "Unknown or Auto";
  409. else if($data==1) $data = "Daylight";
  410. else if($data==2) $data = "Fluorescent";
  411. else if($data==3) $data = "Tungsten (Incandescent light)";
  412. else if($data==4) $data = "Flash";
  413. else if($data==9) $data = "Fine Weather";
  414. else if($data==10) $data = "Cloudy Weather";
  415. else if($data==11) $data = "Shade";
  416. else if($data==12) $data = "Daylight Fluorescent (D 5700 - 7100K)";
  417. else if($data==13) $data = "Day White Fluorescent (N 4600 - 5400K)";
  418. else if($data==14) $data = "Cool White Fluorescent (W 3900 -4500K)";
  419. else if($data==15) $data = "White Fluorescent (WW 3200 - 3700K)";
  420. else if($data==10) $data = "Flash";
  421. else if($data==17) $data = "Standard Light A";
  422. else if($data==18) $data = "Standard Light B";
  423. else if($data==19) $data = "Standard Light C";
  424. else if($data==20) $data = "D55";
  425. else if($data==21) $data = "D65";
  426. else if($data==22) $data = "D75";
  427. else if($data==23) $data = "D50";
  428. else if($data==24) $data = "ISO Studio Tungsten";
  429. else if($data==255) $data = "Other";
  430. //else $data = "Unknown: ".$data;
  431. else $data = "Unknown";
  432. } else if($tag=="9209") { //Flash
  433. if($data==0) $data = "No Flash";
  434. else if($data==1) $data = "Flash";
  435. else if($data==5) $data = "Flash, strobe return light not detected";
  436. else if($data==7) $data = "Flash, strobe return light detected";
  437. else if($data==9) $data = "Compulsory Flash";
  438. else if($data==13) $data = "Compulsory Flash, Return light not detected";
  439. else if($data==15) $data = "Compulsory Flash, Return light detected";
  440. else if($data==16) $data = "No Flash";
  441. else if($data==24) $data = "No Flash";
  442. else if($data==25) $data = "Flash, Auto-Mode";
  443. else if($data==29) $data = "Flash, Auto-Mode, Return light not detected";
  444. else if($data==31) $data = "Flash, Auto-Mode, Return light detected";
  445. else if($data==32) $data = "No Flash";
  446. else if($data==65) $data = "Red Eye";
  447. else if($data==69) $data = "Red Eye, Return light not detected";
  448. else if($data==71) $data = "Red Eye, Return light detected";
  449. else if($data==73) $data = "Red Eye, Compulsory Flash";
  450. else if($data==77) $data = "Red Eye, Compulsory Flash, Return light not detected";
  451. else if($data==79) $data = "Red Eye, Compulsory Flash, Return light detected";
  452. else if($data==89) $data = "Red Eye, Auto-Mode";
  453. else if($data==93) $data = "Red Eye, Auto-Mode, Return light not detected";
  454. else if($data==95) $data = "Red Eye, Auto-Mode, Return light detected";
  455. //else $data = "Unknown: ".$data;
  456. else $data = "Unknown";
  457. } else if($tag=="a001") { //ColorSpace
  458. if($data==1) $data = "sRGB";
  459. else $data = "Uncalibrated";
  460. } else if($tag=="a002" || $tag=="a003") { //ExifImageWidth/Height
  461. $data = $data. " pixels";
  462. } else if($tag=="0103") { //Compression
  463. if($data==1) $data = "No Compression";
  464. else if($data==6) $data = "Jpeg Compression";
  465. //else $data = "Unknown: ".$data;
  466. else $data = "Unknown";
  467. } else if($tag=="a217") { //SensingMethod
  468. if($data==1) $data = "Not defined";
  469. else if($data==2) $data = "One Chip Color Area Sensor";
  470. else if($data==3) $data = "Two Chip Color Area Sensor";
  471. else if($data==4) $data = "Three Chip Color Area Sensor";
  472. else if($data==5) $data = "Color Sequential Area Sensor";
  473. else if($data==7) $data = "Trilinear Sensor";
  474. else if($data==8) $data = "Color Sequential Linear Sensor";
  475. else $data = "Unknown: ".$data;
  476. } else if($tag=="0106") { //PhotometricInterpretation
  477. if($data==1) $data = "Monochrome";
  478. else if($data==2) $data = "RGB";
  479. else if($data==6) $data = "YCbCr";
  480. //else $data = "Unknown: ".$data;
  481. else $data = "Unknown";
  482. }
  483. } else if($type=="UNDEFINED") {
  484. if($tag=="9000" || $tag=="a000" || $tag=="0002") { //ExifVersion,FlashPixVersion,InteroperabilityVersion
  485. $data="version ".$data/100;
  486. }
  487. if($tag=="a300") { //FileSource
  488. $data = bin2hex($data);
  489. $data = str_replace("00","",$data);
  490. $data = str_replace("03","Digital Still Camera",$data);
  491. }
  492. if($tag=="a301") { //SceneType
  493. $data = bin2hex($data);
  494. $data = str_replace("00","",$data);
  495. $data = str_replace("01","Directly Photographed",$data);
  496. }
  497. if($tag=="9101") { //ComponentsConfiguration
  498. $data = bin2hex($data);
  499. $data = str_replace("01","Y",$data);
  500. $data = str_replace("02","Cb",$data);
  501. $data = str_replace("03","Cr",$data);
  502. $data = str_replace("04","R",$data);
  503. $data = str_replace("05","G",$data);
  504. $data = str_replace("06","B",$data);
  505. $data = str_replace("00","",$data);
  506. }
  507. if($tag=="9286") { //UserComment
  508. $encoding = rtrim(substr($data, 0, 8));
  509. $data = rtrim(substr($data, 8));
  510. }
  511. } else {
  512. $data = bin2hex($data);
  513. if($intel==1) $data = intel2Moto($data);
  514. }
  515. return $data;
  516. }
  517. //================================================================================================
  518. //================================================================================================
  519. // Reads one standard IFD entry
  520. //================================================================================================
  521. //================================================================================================
  522. function read_entry(&$result,$in,$seek,$intel,$ifd_name,$globalOffset) {
  523. if(feof($in)) { //test to make sure we can still read.
  524. $result['Errors'] = $result['Errors']+1;
  525. return;
  526. }
  527. //2 byte tag
  528. $tag = bin2hex(fread( $in, 2 ));
  529. if($intel==1) $tag = intel2Moto($tag);
  530. $tag_name = lookup_tag($tag);
  531. //2 byte datatype
  532. $type = bin2hex(fread( $in, 2 ));
  533. if($intel==1) $type = intel2Moto($type);
  534. lookup_type($type,$size);
  535. //4 byte number of elements
  536. $count = bin2hex(fread( $in, 4 ));
  537. if($intel==1) $count = intel2Moto($count);
  538. $bytesofdata = $size*hexdec($count);
  539. //4 byte value or pointer to value if larger than 4 bytes
  540. $value = fread( $in, 4 );
  541. if($bytesofdata<=4) { //if datatype is 4 bytes or less, its the value
  542. $data = $value;
  543. } else if($bytesofdata<100000) { //otherwise its a pointer to the value, so lets go get it
  544. $value = bin2hex($value);
  545. if($intel==1) $value = intel2Moto($value);
  546. $v = fseek($seek,$globalOffset+hexdec($value)); //offsets are from TIFF header which is 12 bytes from the start of the file
  547. if($v==0) {
  548. $data = fread($seek, $bytesofdata);
  549. } else if($v==-1) {
  550. $result['Errors'] = $result['Errors']+1;
  551. }
  552. } else { //bytesofdata was too big, so the exif had an error
  553. $result['Errors'] = $result['Errors']+1;
  554. return;
  555. }
  556. if($tag_name=="MakerNote") { //if its a maker tag, we need to parse this specially
  557. $make = $result['IFD0']['Make'];
  558. if($result['VerboseOutput']==1) {
  559. $result[$ifd_name]['MakerNote']['RawData'] = $data;
  560. }
  561. if(eregi("NIKON",$make)) {
  562. require_once('makers/nikon.php');
  563. parseNikon($data,$result);
  564. $result[$ifd_name]['KnownMaker'] = 1;
  565. } else if(eregi("OLYMPUS",$make)) {
  566. require_once('makers/olympus.php');
  567. parseOlympus($data,$result,$seek,$globalOffset);
  568. $result[$ifd_name]['KnownMaker'] = 1;
  569. } else if(eregi("Canon",$make)) {
  570. require_once('makers/canon.php');
  571. parseCanon($data,$result,$seek,$globalOffset);
  572. $result[$ifd_name]['KnownMaker'] = 1;
  573. } else if(eregi("FUJIFILM",$make)) {
  574. require_once('makers/fujifilm.php');
  575. parseFujifilm($data,$result);
  576. $result[$ifd_name]['KnownMaker'] = 1;
  577. } else if(eregi("SANYO",$make)) {
  578. require_once('makers/sanyo.php');
  579. parseSanyo($data,$result,$seek,$globalOffset);
  580. $result[$ifd_name]['KnownMaker'] = 1;
  581. } else {
  582. $result[$ifd_name]['KnownMaker'] = 0;
  583. }
  584. } else if($tag_name=="GPSInfoOffset") {
  585. require_once('makers/gps.php');
  586. $formated_data = formatData($type,$tag,$intel,$data);
  587. $result[$ifd_name]['GPSInfo'] = $formated_data;
  588. parseGPS($data,$result,$formated_data,$seek,$globalOffset);
  589. } else {
  590. //Format the data depending on the type and tag
  591. $formated_data = formatData($type,$tag,$intel,$data);
  592. // added by schonhose to remove all unknown tags and possible binary stuff
  593. if ($tag_name=="unknown"){
  594. $result[$ifd_name][$tag_name] = "unknown";
  595. }else{
  596. $result[$ifd_name][$tag_name] = $formated_data;
  597. }
  598. if($result['VerboseOutput']==1) {
  599. if($type=="URATIONAL" || $type=="SRATIONAL" || $type=="USHORT" || $type=="SSHORT" || $type=="ULONG" || $type=="SLONG" || $type=="FLOAT" || $type=="DOUBLE") {
  600. $data = bin2hex($data);
  601. if($intel==1) $data = intel2Moto($data);
  602. }
  603. $result[$ifd_name][$tag_name."_Verbose"]['RawData'] = $data;
  604. $result[$ifd_name][$tag_name."_Verbose"]['Type'] = $type;
  605. $result[$ifd_name][$tag_name."_Verbose"]['Bytes'] = $bytesofdata;
  606. }
  607. }
  608. }
  609. //================================================================================================
  610. //================================================================================================
  611. // Pass in a file and this reads the EXIF data
  612. //
  613. // Usefull resources
  614. // http://www.ba.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
  615. // http://www.w3.org/Graphics/JPEG/jfif.txt
  616. // http://exif.org/
  617. // http://www.ozhiker.com/electronics/pjmt/library/list_contents.php4
  618. // http://www.ozhiker.com/electronics/pjmt/jpeg_info/makernotes.html
  619. // http://pel.sourceforge.net/
  620. // http://us2.php.net/manual/en/function.exif-read-data.php
  621. //================================================================================================
  622. //================================================================================================
  623. function read_exif_data_raw($path,$verbose) {
  624. if($path=='' || $path=='none') return;
  625. $in = @fopen($path, "rb"); //the b is for windows machines to open in binary mode
  626. $seek = @fopen($path, "rb"); //There may be an elegant way to do this with one file handle.
  627. $globalOffset = 0;
  628. if(!isset($verbose)) $verbose=0;
  629. $result['VerboseOutput'] = $verbose;
  630. $result['Errors'] = 0;
  631. if(!$in || !$seek) { //if the path was invalid, this error will catch it
  632. $result['Errors'] = 1;
  633. $result['Error'][$result['Errors']] = "The file could not be found.";
  634. return $result;
  635. }
  636. $GLOBALS['exiferFileSize'] = filesize($path);
  637. //First 2 bytes of JPEG are 0xFFD8
  638. $data = bin2hex(fread( $in, 2 ));
  639. if($data=="ffd8") {
  640. $result['ValidJpeg'] = 1;
  641. } else {
  642. $result['ValidJpeg'] = 0;
  643. fclose($in);
  644. fclose($seek);
  645. return $result;
  646. }
  647. $result['ValidIPTCData'] = 0;
  648. $result['ValidJFIFData'] = 0;
  649. $result['ValidEXIFData'] = 0;
  650. $result['ValidAPP2Data'] = 0;
  651. $result['ValidCOMData'] = 0;
  652. //Next 2 bytes are MARKER tag (0xFFE#)
  653. $data = bin2hex(fread( $in, 2 ));
  654. $size = bin2hex(fread( $in, 2 ));
  655. //LOOP THROUGH MARKERS TILL YOU GET TO FFE1 (exif marker)
  656. //
  657. $abortCount = 0;
  658. while(!feof($in) && $data!="ffe1" && $data!="ffc0" && $data!="ffd9" && ++$abortCount < 200) {
  659. if($data=="ffe0") { //JFIF Marker
  660. // $result lines commented out because it causes trouble in the way Pixelpost handles exif.
  661. //$result['ValidJFIFData'] = 1;
  662. //$result['JFIF']['Size'] = hexdec($size);
  663. if(hexdec($size)-2 > 0) {
  664. $data = fread( $in, hexdec($size)-2);
  665. //$result['JFIF']['Data'] = $data;
  666. }
  667. //$result['JFIF']['Identifier'] = substr($data,0,5);;
  668. //$result['JFIF']['ExtensionCode'] = bin2hex(substr($data,6,1));
  669. $globalOffset+=hexdec($size)+2;
  670. } else if($data=="ffed") { //IPTC Marker
  671. // $result lines commented out because it causes trouble in the way Pixelpost handles exif.
  672. //$result['ValidIPTCData'] = 1;
  673. //$result['IPTC']['Size'] = hexdec($size);
  674. if(hexdec($size)-2 > 0) {
  675. $data = fread( $in, hexdec($size)-2);
  676. //$result['IPTC']['Data'] = $data ;
  677. }
  678. $globalOffset+=hexdec($size)+2;
  679. } else if($data=="ffe2") { //EXIF extension Marker
  680. // $result lines commented out because it causes trouble in the way Pixelpost handles exif.
  681. //$result['ValidAPP2Data'] = 1;
  682. //$result['APP2']['Size'] = hexdec($size);
  683. if(hexdec($size)-2 > 0) {
  684. $data = fread( $in, hexdec($size)-2);
  685. //$result['APP2']['Data'] = $data ;
  686. }
  687. $globalOffset+=hexdec($size)+2;
  688. } else if($data=="fffe") { //COM extension Marker
  689. // $result lines commented out because it causes trouble in the way Pixelpost handles exif.
  690. //$result['ValidCOMData'] = 1;
  691. //$result['COM']['Size'] = hexdec($size);
  692. if(hexdec($size)-2 > 0) {
  693. $data = fread( $in, hexdec($size)-2);
  694. //$result['COM']['Data'] = $data ;
  695. }
  696. $globalOffset+=hexdec($size)+2;
  697. } else if($data=="ffe1") {
  698. $result['ValidEXIFData'] = 1;
  699. }
  700. $data = bin2hex(fread( $in, 2 ));
  701. $size = bin2hex(fread( $in, 2 ));
  702. }
  703. //END MARKER LOOP
  704. if($data=="ffe1") {
  705. $result['ValidEXIFData'] = 1;
  706. } else {
  707. fclose($in);
  708. fclose($seek);
  709. return $result;
  710. }
  711. //Size of APP1
  712. $result['APP1Size'] = hexdec($size);
  713. //Start of APP1 block starts with "Exif" header (6 bytes)
  714. $header = fread( $in, 6 );
  715. //Then theres a TIFF header with 2 bytes of endieness (II or MM)
  716. $header = fread( $in, 2 );
  717. if($header==="II") {
  718. $intel=1;
  719. $result['Endien'] = "Intel";
  720. } else if($header==="MM") {
  721. $intel=0;
  722. $result['Endien'] = "Motorola";
  723. } else {
  724. $intel=1; //not sure what the default should be, but this seems reasonable
  725. $result['Endien'] = "Unknown";
  726. }
  727. //2 bytes of 0x002a
  728. $tag = bin2hex(fread( $in, 2 ));
  729. //Then 4 bytes of offset to IFD0 (usually 8 which includes all 8 bytes of TIFF header)
  730. $offset = bin2hex(fread( $in, 4 ));
  731. if($intel==1) $offset = intel2Moto($offset);
  732. // Check for extremely large values here
  733. if(hexdec($offset) > 100000) {
  734. $result['ValidEXIFData'] = 0;
  735. fclose($in);
  736. fclose($seek);
  737. return $result;
  738. }
  739. if(hexdec($offset)>8) $unknown = fread( $in, hexdec($offset)-8); //fixed this bug in 1.3
  740. //add 12 to the offset to account for TIFF header
  741. $globalOffset+=12;
  742. //===========================================================Start of IFD0
  743. $num = bin2hex(fread( $in, 2 ));
  744. if($intel==1) $num = intel2Moto($num);
  745. $num = hexdec($num);
  746. $result['IFD0NumTags'] = $num;
  747. if($num<1000) { //1000 entries is too much and is probably an error.
  748. for($i=0;$i<$num;$i++) {
  749. read_entry($result,$in,$seek,$intel,"IFD0",$globalOffset);
  750. }
  751. } else {
  752. $result['Errors'] = $result['Errors']+1;
  753. $result['Error'][$result['Errors']] = "Illegal size for IFD0";
  754. }
  755. //store offset to IFD1
  756. $offset = bin2hex(fread( $in, 4 ));
  757. if($intel==1) $offset = intel2Moto($offset);
  758. $result['IFD1Offset'] = hexdec($offset);
  759. //Check for SubIFD
  760. if(!isset($result['IFD0']['ExifOffset']) || $result['IFD0']['ExifOffset']==0) {
  761. fclose($in);
  762. fclose($seek);
  763. return $result;
  764. }
  765. //seek to SubIFD (Value of ExifOffset tag) above.
  766. $ExitOffset = $result['IFD0']['ExifOffset'];
  767. $v = fseek($in,$globalOffset+$ExitOffset);
  768. if($v==-1) {
  769. $result['Errors'] = $result['Errors']+1;
  770. $result['Error'][$result['Errors']] = "Couldnt Find SubIFD";
  771. }
  772. //===========================================================Start of SubIFD
  773. $num = bin2hex(fread( $in, 2 ));
  774. if($intel==1) $num = intel2Moto($num);
  775. $num = hexdec($num);
  776. $result['SubIFDNumTags'] = $num;
  777. if($num<1000) { //1000 entries is too much and is probably an error.
  778. for($i=0;$i<$num;$i++) {
  779. read_entry($result,$in,$seek,$intel,"SubIFD",$globalOffset);
  780. }
  781. } else {
  782. $result['Errors'] = $result['Errors']+1;
  783. $result['Error'][$result['Errors']] = "Illegal size for SubIFD";
  784. }
  785. //Check for IFD1
  786. if(!isset($result['IFD1Offset']) || $result['IFD1Offset']==0) {
  787. fclose($in);
  788. fclose($seek);
  789. return $result;
  790. }
  791. //seek to IFD1
  792. $v = fseek($in,$globalOffset+$result['IFD1Offset']);
  793. if($v==-1) {
  794. $result['Errors'] = $result['Errors']+1;
  795. $result['Error'][$result['Errors']] = "Couldnt Find IFD1";
  796. }
  797. //===========================================================Start of IFD1
  798. $num = bin2hex(fread( $in, 2 ));
  799. if($intel==1) $num = intel2Moto($num);
  800. $num = hexdec($num);
  801. $result['IFD1NumTags'] = $num;
  802. if($num<1000) { //1000 entries is too much and is probably an error.
  803. for($i=0;$i<$num;$i++) {
  804. read_entry($result,$in,$seek,$intel,"IFD1",$globalOffset);
  805. }
  806. } else {
  807. $result['Errors'] = $result['Errors']+1;
  808. $result['Error'][$result['Errors']] = "Illegal size for IFD1";
  809. }
  810. //if verbose output is on, stick in the thumbnail raw data
  811. if($result['VerboseOutput']==1 && $result['IFD1']['JpegIFOffset']>0 && $result['IFD1']['JpegIFByteCount']>0) {
  812. $v = fseek($seek,$globalOffset+$result['IFD1']['JpegIFOffset']);
  813. if($v==0) {
  814. $data = fread($seek, $result['IFD1']['JpegIFByteCount']);
  815. } else if($v==-1) {
  816. $result['Errors'] = $result['Errors']+1;
  817. }
  818. $result['IFD1']["ThumbnailData"] = $data;
  819. }
  820. //Check for Interoperability IFD
  821. if(!isset($result['SubIFD']['ExifInteroperabilityOffset']) || $result['SubIFD']['ExifInteroperabilityOffset']==0) {
  822. fclose($in);
  823. fclose($seek);
  824. return $result;
  825. }
  826. //seek to InteroperabilityIFD
  827. $v = fseek($in,$globalOffset+$result['SubIFD']['ExifInteroperabilityOffset']);
  828. if($v==-1) {
  829. $result['Errors'] = $result['Errors']+1;
  830. $result['Error'][$result['Errors']] = "Couldnt Find InteroperabilityIFD";
  831. }
  832. //===========================================================Start of InteroperabilityIFD
  833. $num = bin2hex(fread( $in, 2 ));
  834. if($intel==1) $num = intel2Moto($num);
  835. $num = hexdec($num);
  836. $result['InteroperabilityIFDNumTags'] = $num;
  837. if($num<1000) { //1000 entries is too much and is probably an error.
  838. for($i=0;$i<$num;$i++) {
  839. read_entry($result,$in,$seek,$intel,"InteroperabilityIFD",$globalOffset);
  840. }
  841. } else {
  842. $result['Errors'] = $result['Errors']+1;
  843. $result['Error'][$result['Errors']] = "Illegal size for InteroperabilityIFD";
  844. }
  845. fclose($in);
  846. fclose($seek);
  847. return $result;
  848. }
  849. //================================================================================================
  850. //================================================================================================
  851. // Converts a floating point number into a fraction. Many thanks to Matthieu Froment for this code
  852. //================================================================================================
  853. //================================================================================================
  854. function ConvertToFraction($v, &$n, &$d)
  855. {
  856. $MaxTerms = 15; //Limit to prevent infinite loop
  857. $MinDivisor = 0.000001; //Limit to prevent divide by zero
  858. $MaxError = 0.00000001; //How close is enough
  859. $f = $v; //Initialize fraction being converted
  860. $n_un = 1; //Initialize fractions with 1/0, 0/1
  861. $d_un = 0;
  862. $n_deux = 0;
  863. $d_deux = 1;
  864. for ($i = 0;$i<$MaxTerms;$i++)
  865. {
  866. $a = floor($f); //Get next term
  867. $f = $f - $a; //Get new divisor
  868. $n = $n_un * $a + $n_deux; //Calculate new fraction
  869. $d = $d_un * $a + $d_deux;
  870. $n_deux = $n_un; //Save last two fractions
  871. $d_deux = $d_un;
  872. $n_un = $n;
  873. $d_un = $d;
  874. if ($f < $MinDivisor) //Quit if dividing by zero
  875. break;
  876. if (abs($v - $n / $d) < $MaxError)
  877. break;
  878. $f = 1 / $f; //Take reciprocal
  879. }
  880. }
  881. ?>