Source for file exif.inc
Documentation is available at exif.inc
1 <?php 2 /** 3 * PHP Class to read, write and transfer EXIF information 4 * that most of the digital camera produces. 5 * 6 * This class is based on jhead (in C) by Matthias Wandel 7 * 8 * Vinay Yadav (vinayRas) < vinay@sanisoft.com > 9 * http://www.sanisoft.com/phpexifrw/ 10 * 11 * For more information on EXIF 12 * http://www.pima.net/standards/it10/PIMA15740/exif.htm 13 * 14 * Features: 15 * - Read Exif Information 16 * - Extract and display emdedded thumbnails 17 * - Transfer Exif Information 18 * - Add Comments to files. 19 * - Tranfering EXIF information from one file to another. 20 * 21 * TODO 22 * 1. Writing exif information to the file. 23 * 2. Add EXIF audio reading methods (I think it exists!) 24 * 3. Support of additional tags. 25 * 4. Handling Unicode character in UserComment tag of EXif Information. 26 */ 27 28 /** * Start Of Frame N */ 28 29 define("M_SOF0",0xC0); 30 /** * N indicates which compression process */ 30 31 define("M_SOF1",0xC1); 32 /** * Only SOF0-SOF2 are now in common use */ 32 33 define("M_SOF2",0xC2); 34 /** * */ 34 35 define("M_SOF3",0xC3); 36 /** * NB: codes C4 and CC are NOT SOF markers */ 36 37 define("M_SOF5",0xC5); 38 /** * */ 38 39 define("M_SOF6",0xC6); 40 /** * */ 40 41 define("M_SOF7",0xC7); 42 /** * */ 42 43 define("M_SOF9",0xC9); 44 /** * */ 44 45 define("M_SOF10",0xCA); 46 /** * */ 46 47 define("M_SOF11",0xCB); 48 /** * */ 48 49 define("M_SOF13",0xCD); 50 /** * */ 50 51 define("M_SOF14",0xCE); 52 /** * */ 52 53 define("M_SOF15",0xCF); 54 /** * Start Of Image (beginning of datastream) */ 54 55 define("M_SOI",0xD8); 56 /** * End Of Image (end of datastream) */ 56 57 define("M_EOI",0xD9); 58 /** * Start Of Scan (begins compressed data) */ 58 59 define("M_SOS",0xDA); 60 /** * Jfif marker */ 60 61 define("M_JFIF",0xE0); 62 /** * Exif marker */ 62 63 define("M_EXIF",0xE1); 64 /** * Image Title */ 64 65 define("M_COM",0xFE); 66 67 define("NUM_FORMATS","12"); 68 69 /** * Tag Data Format */ 69 70 define("FMT_BYTE","1"); 71 /** * ASCII */ 71 72 define("FMT_STRING","2"); 73 /** * Short */ 73 74 define("FMT_USHORT","3"); 75 /** * Long */ 75 76 define("FMT_ULONG","4"); 77 /** * Rational */ 77 78 define("FMT_URATIONAL","5"); 79 /** * Byte */ 79 80 define("FMT_SBYTE","6"); 81 /** * Undefined */ 81 82 define("FMT_UNDEFINED","7"); 83 /** * Short */ 83 84 define("FMT_SSHORT","8"); 85 /** * Long */ 85 86 define("FMT_SLONG","9"); 87 /** * Rational */ 87 88 define("FMT_SRATIONAL","10"); 89 /** * Single */ 89 90 define("FMT_SINGLE","11"); 91 /** * Double */ 91 92 define("FMT_DOUBLE","12"); 93 94 /** * Exif IFD */ 94 95 define("TAG_EXIF_OFFSET","0x8769"); 96 /** * Interoperability tag */ 96 97 define("TAG_INTEROP_OFFSET","0xa005"); 98 /** * Image input equipment manufacturer */ 98 99 define("TAG_MAKE","0x010F"); 100 /** * Image input equipment model */ 100 101 define("TAG_MODEL","0x0110"); 102 /** * Orientation of image */ 102 103 define("TAG_ORIENTATION","0x0112"); 104 /** * Exposure Time */ 104 105 define("TAG_EXPOSURETIME","0x829A"); 106 /** * F Number */ 106 107 define("TAG_FNUMBER","0x829D"); 108 /** * Shutter Speed */ 108 109 define("TAG_SHUTTERSPEED","0x9201"); 110 /** * Aperture */ 110 111 define("TAG_APERTURE","0x9202"); 112 /** * Aperture */ 112 113 define("TAG_MAXAPERTURE","0x9205"); 114 /** * Lens Focal Length */ 114 115 define("TAG_FOCALLENGTH","0x920A"); 116 /** * The date and time when the original image data was generated. */ 116 117 define("TAG_DATETIME_ORIGINAL","0x9003"); 118 /** * User Comments */ 118 119 define("TAG_USERCOMMENT","0x9286"); 120 /** * subject Location */ 120 121 define("TAG_SUBJECT_DISTANCE","0x9206"); 122 /** * Flash */ 122 123 define("TAG_FLASH","0x9209"); 124 /** * Focal Plane X Resolution */ 124 125 define("TAG_FOCALPLANEXRES","0xa20E"); 126 /** * Focal Plane Resolution Units */ 126 127 define("TAG_FOCALPLANEUNITS","0xa210"); 128 /** * Image Width */ 128 129 define("TAG_EXIF_IMAGEWIDTH","0xA002"); 130 /** * Image Height */ 130 131 define("TAG_EXIF_IMAGELENGTH","0xA003"); 132 /** * Exposure Bias */ 132 133 define("TAG_EXPOSURE_BIAS","0x9204"); 134 /** * Light Source */ 134 135 define("TAG_WHITEBALANCE","0x9208"); 136 /** * Metering Mode */ 136 137 define("TAG_METERING_MODE","0x9207"); 138 /** * Exposure Program */ 138 139 define("TAG_EXPOSURE_PROGRAM","0x8822"); 140 /** * ISO Equivalent Speed Rating */ 140 141 define("TAG_ISO_EQUIVALENT","0x8827"); 142 /** * Compressed Bits Per Pixel */ 142 143 define("TAG_COMPRESSION_LEVEL","0x9102"); 144 /** * Thumbnail Start Offset */ 144 145 define("TAG_THUMBNAIL_OFFSET","0x0201"); 146 /** * Thumbnail Length */ 146 147 define("TAG_THUMBNAIL_LENGTH","0x0202"); 148 /** * Image Marker */ 148 149 define("PSEUDO_IMAGE_MARKER",0x123); 150 /** * Max Image Title Length */ 150 151 define("MAX_COMMENT",2000); 152 153 /** 154 * As more and more tags will be added will, the contents of array will increase. 155 * DONT remove any blank array, since they do contain several tags. 156 */ 157 158 $FMT_BYTE_ARRAY = array(); 159 $FMT_STRING_ARRAY = array( 160 0x010E, //Image title 161 0x010F, // Make - Image input equipment manufacturer 162 0x0110, // Model - Image input equipment model 163 0x0131, // Software - Software used 164 0x013B, // Artist - Person who created the image 165 0x8298,// Copyright - Copyright holder 166 0x9003, // DateTimeOriginal - Date and time of original data generation 167 ); 168 $FMT_USHORT_ARRAY = array( 169 0x0112, // Orientation 170 0x8822, // Exposure Program 171 0x9207, // Metering mode 172 0x9209, // Flash 173 0xA002, // Valid image width PixelXDimension 174 0xA003, // Valid image height PixelYDimension 175 ); 176 $FMT_ULONG_ARRAY = array( 177 0x0202, // JPEGInterchangeFormatLength 178 ); 179 $FMT_URATIONAL_ARRAY = array( 180 0x829A, // Exposure Time 181 0x829D, // F Number 182 0x9102, // CompressedBitsPerPixel 183 0x9202, // Aperture 184 0x9205, // MaxApertureValue 185 0x920A, // focal length 186 ); 187 $FMT_SBYTE_ARRAY = array(); 188 $FMT_UNDEFINED_ARRAY = array(); 189 $FMT_SSHORT_ARRAY = array(); 190 $FMT_SLONG_ARRAY = array(); 191 $FMT_SRATIONAL_ARRAY = array( 192 0x9201, // shutter speed 193 0x9204, // Exposure Bias 194 ); 195 $FMT_SINGLE_ARRAY = array(); 196 $FMT_DOUBLE_ARRAY = array(); 197 198 /** error Description */ 198 199 /** 200 1 - File does not exists! 201 2 - 202 3 - Filename not provided 203 204 10 - too many padding bytes 205 11 - "invalid marker" 206 12 - Premature end of file? 207 208 209 51 - "Illegal subdirectory link" 210 52 - "NOT EXIF FORMAT" 211 53 - "Invalid Exif alignment marker.\n" 212 54 - "Invalid Exif start (1)" 213 214 */ 215 216 $TagTable = array( 217 array( 0x100, "ImageWidth"), 218 array( 0x101, "ImageLength"), 219 array( 0x102, "BitsPerSample"), 220 array( 0x103, "Compression"), 221 array( 0x106, "PhotometricInterpretation"), 222 array( 0x10A, "FillOrder"), 223 array( 0x10D, "DocumentName"), 224 array( 0x10E, "ImageDescription"), 225 array( 0x10F, "Make"), 226 array( 0x110, "Model"), 227 array( 0x111, "StripOffsets"), 228 array( 0x112, "Orientation"), 229 array( 0x115, "SamplesPerPixel"), 230 array( 0x116, "RowsPerStrip"), 231 array( 0x117, "StripByteCounts"), 232 array( 0x11A, "XResolution"), 233 array( 0x11B, "YResolution"), 234 array( 0x11C, "PlanarConfiguration"), 235 array( 0x128, "ResolutionUnit"), 236 array( 0x12D, "TransferFunction"), 237 array( 0x131, "Software"), 238 array( 0x132, "DateTime"), 239 array( 0x13B, "Artist"), 240 array( 0x13E, "WhitePoint"), 241 array( 0x13F, "PrimaryChromaticities"), 242 array( 0x156, "TransferRange"), 243 array( 0x200, "JPEGProc"), 244 array( 0x201, "ThumbnailOffset"), 245 array( 0x202, "ThumbnailLength"), 246 array( 0x211, "YCbCrCoefficients"), 247 array( 0x212, "YCbCrSubSampling"), 248 array( 0x213, "YCbCrPositioning"), 249 array( 0x214, "ReferenceBlackWhite"), 250 array( 0x828D, "CFARepeatPatternDim"), 251 array( 0x828E, "CFAPattern"), 252 array( 0x828F, "BatteryLevel"), 253 array( 0x8298, "Copyright"), 254 array( 0x829A, "ExposureTime"), 255 array( 0x829D, "FNumber"), 256 array( 0x83BB, "IPTC/NAA"), 257 array( 0x8769, "ExifOffset"), 258 array( 0x8773, "InterColorProfile"), 259 array( 0x8822, "ExposureProgram"), 260 array( 0x8824, "SpectralSensitivity"), 261 array( 0x8825, "GPSInfo"), 262 array( 0x8827, "ISOSpeedRatings"), 263 array( 0x8828, "OECF"), 264 array( 0x9000, "ExifVersion"), 265 array( 0x9003, "DateTimeOriginal"), 266 array( 0x9004, "DateTimeDigitized"), 267 array( 0x9101, "ComponentsConfiguration"), 268 array( 0x9102, "CompressedBitsPerPixel"), 269 array( 0x9201, "ShutterSpeedValue"), 270 array( 0x9202, "ApertureValue"), 271 array( 0x9203, "BrightnessValue"), 272 array( 0x9204, "ExposureBiasValue"), 273 array( 0x9205, "MaxApertureValue"), 274 array( 0x9206, "SubjectDistance"), 275 array( 0x9207, "MeteringMode"), 276 array( 0x9208, "LightSource"), 277 array( 0x9209, "Flash"), 278 array( 0x920A, "FocalLength"), 279 array( 0x927C, "MakerNote"), 280 array( 0x9286, "UserComment"), 281 array( 0x9290, "SubSecTime"), 282 array( 0x9291, "SubSecTimeOriginal"), 283 array( 0x9292, "SubSecTimeDigitized"), 284 array( 0xA000, "FlashPixVersion"), 285 array( 0xA001, "ColorSpace"), 286 array( 0xA002, "ExifImageWidth"), 287 array( 0xA003, "ExifImageLength"), 288 array( 0xA005, "InteroperabilityOffset"), 289 array( 0xA20B, "FlashEnergy"), // 0x920B in TIFF/EP 290 array( 0xA20C, "SpatialFrequencyResponse"), // 0x920C - - 291 array( 0xA20E, "FocalPlaneXResolution"), // 0x920E - - 292 array( 0xA20F, "FocalPlaneYResolution"), // 0x920F - - 293 array( 0xA210, "FocalPlaneResolutionUnit"), // 0x9210 - - 294 array( 0xA214, "SubjectLocation"), // 0x9214 - - 295 array( 0xA215, "ExposureIndex"), // 0x9215 - - 296 array( 0xA217, "SensingMethod"), // 0x9217 - - 297 array( 0xA300, "FileSource"), 298 array( 0xA301, "SceneType"), 299 array( 0, NULL) 300 ) ; 301 302 $ProcessTable = array( 303 array(M_SOF0, "Baseline"), 304 array(M_SOF1, "Extended sequential"), 305 array(M_SOF2, "Progressive"), 306 array(M_SOF3, "Lossless"), 307 array(M_SOF5, "Differential sequential"), 308 array(M_SOF6, "Differential progressive"), 309 array(M_SOF7, "Differential lossless"), 310 array(M_SOF9, "Extended sequential, arithmetic coding"), 311 array(M_SOF10, "Progressive, arithmetic coding"), 312 array(M_SOF11, "Lossless, arithmetic coding"), 313 array(M_SOF13, "Differential sequential, arithmetic coding"), 314 array(M_SOF14, "Differential progressive, arithmetic coding"), 315 array(M_SOF15, "Differential lossless, arithmetic coding"), 316 array(0, "Unknown") 317 ); 318 319 /** 320 * PHP Class to read, write and transfer EXIF information 321 * that most of the digital camera produces 322 * Currenty it can only read JPEG file. 323 */ 324 /** 325 * @author Vinay Yadav (vinayRas) < vinay@sanisoft.com > 326 * 327 * @todo Writing exif information to the file. 328 * @todo Add EXIF audio reading methods (I think it exists!) 329 * @todo Support of additional tags. 330 * @todo Handling Unicode character in UserComment tag of EXif Information. 331 * 332 * @version 0.5 333 * @licence http://opensource.org/licenses/lgpl-license.php GNU LGPL 334 */ 335 class phpExifRW { 336 337 /* 338 * Array containg all Exif and JPEG image attributes 339 * into regular expressions for themselves. 340 * $ImageInfo[TAG] = TAG_VALUE; 341 * 342 * @var array 343 * @access private 344 * 345 */ 346 var $ImageInfo = array(); 347 348 var $MotorolaOrder = 0; 349 var $ExifImageWidth = 0; // 350 var $FocalplaneXRes = 0; // 351 var $FocalplaneUnits = 0; // 352 var $sections = array(); 353 var $currSection = 0; /** Stores total number fo Sections */ 353 354 355 var $BytesPerFormat = array(0,1,1,2,4,8,1,1,2,4,8,4,8); 356 357 var $DirWithThumbnailPtrs = 0; 358 var $ThumbnailSize = 0; 359 360 var $ReadMode = array( 361 "READ_EXIF" => 1, 362 "READ_IMAGE" => 2, 363 "READ_ALL" => 3 364 ); 365 366 var $ImageReadMode = 3; /** related to $RealMode arrays values */ 366 367 var $file = ""; /** JPEG file to parse for EXIF data */ 367 368 var $newFile = 1; /** flag to check if the current file has been parsed or not. */ 368 369 370 var $thumbnail = ""; /* Name of thumbnail */ 371 var $thumbnailURL = ""; /* */ 372 373 var $exifSection = -1; // market the exif section index oout of all sections 374 375 var $errno = 0; 376 var $errstr = ""; 377 378 var $debug = false; 379 var $showTags = false; 380 381 // Caching ralated variables 382 var $caching = true; /* Should cacheing of image thumnails be allowed? */ 383 var $cacheDir = ""; /* Checkout constructor for default path. */ 384 385 /** 386 * Constructor 387 * @param string File name to be parsed. 388 * 389 */ 390 function phpExifRW($file = "") { 391 392 if(!empty($file)) { 393 $this->file = $file; 394 } 395 396 if($this->caching) { 397 $this->cacheDir = dirname(__FILE__)."/.cache_thumbs"; 398 399 /** 400 * If Cache directory does not exists then attempt to create it. 401 */ 402 if(!is_dir($this->cacheDir)) { 403 @mkdir($this->cacheDir); 404 } 405 406 // Prepare the ame of thumbnail 407 if(is_dir($this->cacheDir)) { 408 $this->thumbnail = $this->cacheDir."/".$this->file; 409 $this->thumbnailURL = ".cache_thumbs/$this->file"; 410 } 411 } 412 413 /** check if file exists! */ 414 if(!file_exists($this->file)) { 415 $this->errno = 1; 416 $this->errstr = "File '".$this->file."' does not exists!"; 417 } 418 $this->currSection = 0; 419 } 420 421 /** 422 * Show Debugging information 423 * 424 * @param string Debugging message to display 425 * @param int Type of error (0 - Warning, 1 - Error) 426 * @return void 427 * 428 */ 429 function debug($str,$TYPE = 0) { 430 if($this->debug) { 431 echo "<br>$str"; 432 if($TYPE == 1) { 433 exit; 434 } 435 } 436 } 437 438 /** 439 * Processes the whole file. 440 * 441 */ 442 function processFile() { 443 /** dont reparse the whole file. */ 444 if(!$this->newFile) return true; 445 446 /** Open the JPEG in binary safe reading mode */ 447 $fp = fopen($this->file,"rb"); 448 449 $this->ImageInfo["FileName"] = $this->file; 450 $this->ImageInfo["FileSize"] = filesize($this->file); /** Size of the File */ 451 $this->ImageInfo["FileDateTime"] = filectime($this->file); /** File node change time */ 452 453 /** check whether jped image or not */ 454 $a = fgetc($fp); 455 if (ord($a) != 0xff || ord(fgetc($fp)) != M_SOI){ 456 $this->debug("Not a JPEG FILE",1); 457 $this->errorno = 1; 458 $this->errorstr = "File '".$this->file."' does not exists!"; 459 } 460 $tmpTestLevel = 0; 461 /** Examines each byte one-by-one */ 462 while(!feof($fp)) { 463 $data = array(); 464 for ($a=0;$a<7;$a++){ 465 $marker = fgetc($fp); 466 if (ord($marker) != 0xff) break; 467 if ($a >= 6){ 468 $this->errno = 10; 469 $this->errstr = "too many padding bytes!"; 470 $this->debug($this->errstr,1); 471 return false; 472 } 473 } 474 475 if (ord($marker) == 0xff){ 476 // 0xff is legal padding, but if we get that many, something's wrong. 477 $this->errno = 10; 478 $this->errstr = "too many padding bytes!"; 479 $this->debug($this->errstr,1); 480 } 481 482 $marker = ord($marker); 483 $this->sections[$this->currSection]["type"] = $marker; 484 485 // Read the length of the section. 486 $lh = ord(fgetc($fp)); 487 $ll = ord(fgetc($fp)); 488 489 $itemlen = ($lh << 8) | $ll; 490 491 if ($itemlen < 2){ 492 $this->errno = 11; 493 $this->errstr = "invalid marker"; 494 $this->debug($this->errstr,1); 495 } 496 $this->sections[$this->currSection]["size"] = $itemlen; 497 498 $tmpDataArr = array(); /** Temporary Array */ 499 500 $tmpStr = fread($fp,$itemlen-2); 501 502 $tmpDataArr[] = chr($lh); 503 $tmpDataArr[] = chr($ll); 504 505 $chars = preg_split('//', $tmpStr, -1, PREG_SPLIT_NO_EMPTY); 506 $tmpDataArr = array_merge($tmpDataArr,$chars); 507 508 $data = $tmpDataArr; 509 $this->sections[$this->currSection]["data"] = $data; 510 511 if(count($data) != $itemlen) { 512 $this->errno = 12; 513 $this->errstr = "Premature end of file?"; 514 $this->debug($this->errstr,1); 515 } 516 517 $this->currSection++; /** */ 518 519 switch($marker) { 520 case M_SOS: 521 $this->debug("<br>Found '".M_SOS."' Section, Prcessing it... <br>");; 522 // If reading entire image is requested, read the rest of the data. 523 if ($this->ImageReadMode & $this->ReadMode["READ_IMAGE"]){ 524 // Determine how much file is left. 525 $cp = ftell($fp); 526 fseek($fp,0, SEEK_END); 527 $ep = ftell($fp); 528 fseek($fp, $cp, SEEK_SET); 529 530 $size = $ep-$cp; 531 $got = fread($fp, $size); 532 533 $this->sections[$this->currSection]["data"] = $got; 534 $this->sections[$this->currSection]["size"] = $size; 535 $this->sections[$this->currSection]["type"] = $PSEUDO_IMAGE_MARKER; 536 $this->currSection++; 537 $HaveAll = 1; 538 $exitAll =1; 539 } 540 $this->debug("<br>'".M_SOS."' Section, PROCESSED<br>"); 541 break; 542 case M_COM: // Comment section 543 $this->debug("<br>Found '".M_COM."'(Comment) Section, Processing<br>"); 544 $this->process_COM($data, $itemlen); 545 $this->debug("<br>'".M_COM."'(Comment) Section, PROCESSED<br>"); 546 547 $tmpTestLevel++; 548 break; 549 case M_SOI: 550 $this->debug(" <br> === START OF IMAGE =====<br>"); 551 break; 552 case M_EOI: 553 $this->debug(" <br>=== END OF IMAGE =====<br> "); 554 break; 555 case M_JFIF: 556 // Regular jpegs always have this tag, exif images have the exif 557 // marker instead, althogh ACDsee will write images with both markers. 558 // this program will re-create this marker on absence of exif marker. 559 // hence no need to keep the copy from the file. 560 //echo " <br> === M_JFIF =====<br>"; 561 $this->sections[--$this->currSection]["data"] = ""; 562 break; 563 case M_EXIF: 564 // Seen files from some 'U-lead' software with Vivitar scanner 565 // that uses marker 31 for non exif stuff. Thus make sure 566 // it says 'Exif' in the section before treating it as exif. 567 $this->debug("<br>Found '".M_EXIF."'(Exif) Section, Proccessing<br>"); 568 $this->exifSection = $this->currSection-1; 569 if (($this->ImageReadMode & $this->ReadMode["READ_EXIF"]) && ($data[2].$data[3].$data[4].$data[5]) == "Exif"){ 570 $this->process_EXIF($data, $itemlen); 571 }else{ 572 // Discard this section. 573 $this->sections[--$this->currSection]["data"] = ""; 574 } 575 $this->debug("<br>'".M_EXIF."'(Exif) Section, PROCESSED<br>"); 576 $tmpTestLevel++; 577 break; 578 case M_SOF0: 579 case M_SOF1: 580 case M_SOF2: 581 case M_SOF3: 582 case M_SOF5: 583 case M_SOF6: 584 case M_SOF7: 585 case M_SOF9: 586 case M_SOF10: 587 case M_SOF11: 588 case M_SOF13: 589 case M_SOF14: 590 case M_SOF15: 591 $this->debug("<br>Found M_SOFn Section, Processing<br>"); 592 $this->process_SOFn($data,$marker); 593 $this->debug("<br>M_SOFn Section, PROCESSED<br>"); 594 break; 595 default: 596 $this->debug("DEFAULT: Jpeg section marker 0x$marker x size $itemlen\n"); 597 } 598 $i++; 599 if($exitAll == 1) break; 600 if($tmpTestLevel == 2) break; 601 } 602 fclose($fp); 603 $this->newFile = 0; 604 } 605 606 /** 607 * Changing / Assiging new file 608 * @param string JPEG file to process 609 * 610 */ 611 function assign($file) { 612 613 if(!empty($file)) { 614 $this->file = $file; 615 } 616 617 /** check for existance of file! */ 618 if(!file_exists($this->file)) { 619 $this->errorno = 1; 620 $this->errorstr = "File '".$this->file."' does not exists!"; 621 } 622 $this->newFile = 1; 623 } 624 625 /** 626 * Process SOFn section of Image 627 * @param array An array containing whole section. 628 * @param hex Marker to specify the type of section. 629 * 630 */ 631 function process_SOFn($data,$marker) { 632 $data_precision = 0; 633 $num_components = 0; 634 635 $data_precision = ord($data[2]); 636 637 if($this->debug) { 638 print("Image Dimension Calculation:"); 639 print("((ord($data[3]) << 8) | ord($data[4]));"); 640 } 641 $this->ImageInfo["Height"] = ((ord($data[3]) << 8) | ord($data[4])); 642 $this->ImageInfo["Width"] = ((ord($data[5]) << 8) | ord($data[6])); 643 644 $num_components = ord($data[7]); 645 646 if ($num_components == 3){ 647 $this->ImageInfo["IsColor"] = 1; 648 }else{ 649 $this->ImageInfo["IsColor"] = 0; 650 } 651 652 $this->ImageInfo["Process"] = $marker; 653 $this->debug("JPEG image is $ImageInfo_Width * $ImageInfo_Height, $num_components color components, $data_precision bits per sample\n"); 654 } 655 656 /** 657 * Process Comments 658 * @param array Section data 659 * @param int Length of the section 660 * 661 */ 662 function process_COM($data,$length) { 663 if ($length > MAX_COMMENT) $length = MAX_COMMENT; 664 /** Truncate if it won't fit in our structure. */ 665 666 $nch = 0; 667 for ($a=2;$a<$length;$a++){ 668 $ch = $data[$a]; 669 if ($ch == '\r' && $data[$a+1] == '\n') continue; // Remove cr followed by lf. 670 671 $Comment .= $ch; 672 } 673 $this->ImageInfo[M_COM] = $Comment; 674 $this->debug("COM marker comment: $Comment\n"); 675 } 676 /** 677 * Process one of the nested EXIF directories. 678 * @param string All directory information 679 * @param string whole Section 680 * @param int Length of exif section 681 * 682 */ 683 function ProcessExifDir($DirStart, $OffsetBase, $ExifLength) { 684 global $TagTable 685 686 $NumDirEntries = 0; 687 $ValuePtr = array(); 688 689 $NumDirEntries = $this->Get16u($DirStart[0],$DirStart[1]); 690 691 692 $this->debug("<br>Directory with $NumDirEntries entries\n"); 693 694 for ($de=0;$de<$NumDirEntries;$de++){ 695 $DirEntry = array_slice($DirStart,2+12*$de); 696 697 $Tag = $this->Get16u($DirEntry[0],$DirEntry[1]); 698 $Format = $this->Get16u($DirEntry[2],$DirEntry[3]); 699 $Components = $this->Get32u($DirEntry[4],$DirEntry[5],$DirEntry[6],$DirEntry[7]); 700 701 /** 702 if ((Format-1) >= NUM_FORMATS) { 703 // (-1) catches illegal zero case as unsigned underflows to positive large. 704 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag); 705 continue; 706 } 707 */ 708 709 $ByteCount = $Components * $this->BytesPerFormat[$Format]; 710 711 if ($ByteCount > 4){ 712 $OffsetVal = $this->Get32u($DirEntry[8],$DirEntry[9],$DirEntry[10],$DirEntry[11]); 713 if ($OffsetVal+$ByteCount > $ExifLength){ 714 $this->debug("Illegal value pointer($OffsetVal) for tag $Tag",1); 715 } 716 $ValuePtr = array_slice($OffsetBase,$OffsetVal); 717 } else { 718 $ValuePtr = array_slice($DirEntry,8); 719 } 720 721 /** 722 if (LastExifRefd < ValuePtr+ByteCount){ 723 // Keep track of last byte in the exif header that was actually referenced. 724 // That way, we know where the discardable thumbnail data begins. 725 LastExifRefd = ValuePtr+ByteCount; 726 } 727 */ 728 729 if($this->showTags) { 730 for ($a=0;;$a++){ 731 if ($TagTable[$a][0] == 0){ 732 $this->debug(" Unknown Tag $Tag Value = "); 733 break; 734 } 735 if ($TagTable[$a][0] == $Tag){ 736 $this->debug(" ".$TagTable[$a][1]." ="); 737 break; 738 } 739 } 740 741 switch($Format) { 742 case FMT_UNDEFINED: 743 // Undefined is typically an ascii string. 744 745 case FMT_STRING: 746 // String arrays printed without function call (different from int arrays) 747 { 748 $this->debug("\""); //" 749 $str =""; 750 for ($a=0;$a<$ByteCount;$a++){ 751 $str .= $ValuePtr[$a]; 752 } 753 $this->debug("$str\"\n"); // " 754 755 } 756 break; 757 default: 758 $this->PrintFormatNumber($ValuePtr, $Format, $ByteCount); 759 } // end of switch 760 } // end of if 761 762 // Extract useful components of tag 763 switch($Tag){ 764 765 case TAG_MAKE: 766 $this->ImageInfo[TAG_MAKE]= implode("",array_slice($ValuePtr,0,$ByteCount)); 767 break; 768 769 case TAG_MODEL: 770 $this->ImageInfo[TAG_MODEL] = implode("",array_slice($ValuePtr,0,$ByteCount)); 771 772 break; 773 774 case TAG_DATETIME_ORIGINAL: 775 $this->ImageInfo[TAG_DATETIME_ORIGINAL] = implode("",array_slice($ValuePtr,0,$ByteCount)); 776 $this->ImageInfo["DatePointer"] = implode("",array_slice($ValuePtr,0)); 777 break; 778 779 case TAG_USERCOMMENT: 780 // Olympus has this padded with trailing spaces. Remove these first. 781 for ($a=$ByteCount;;){ 782 $a--; 783 if ($ValuePtr[$a] == ' '){ 784 //$ValuePtr[$a] = '\0'; 785 } else { 786 break; 787 } 788 if ($a == 0) break; 789 } 790 791 // Copy the comment 792 if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4]) == "ASCII"){ 793 for ($a=5;$a<10;$a++){ 794 $c = $ValuePtr[a]; 795 if ($c != '\0' && $c != ' '){ 796 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount)); 797 break; 798 } 799 } 800 } else if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4].$ValuePtr[5].$ValuePtr[6]) == "Unicode"){ 801 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount)); 802 /** 803 * Handle Unicode characters here... 804 */ 805 } else { 806 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount)); 807 } 808 break; 809 810 case TAG_FNUMBER: 811 // Simplest way of expressing aperture, so I trust it the most. 812 // (overwrite previously computd value if there is one) 813 $this->ImageInfo[TAG_FNUMBER] = $this->ConvertAnyFormat(implode("",array_slice($ValuePtr,0)), $Format); 814 break; 815 816 case TAG_APERTURE: 817 case TAG_MAXAPERTURE: 818 // More relevant info always comes earlier, so only use this field if we don't 819 // have appropriate aperture information yet. 820 if ($this->ImageInfo[TAG_MAXAPERTURE] == 0){ 821 $tmpArr = $this->ConvertAnyFormat($ValuePtr, $Format); 822 $this->ImageInfo[TAG_MAXAPERTURE] = exp($tmpArr[0]*log(2)*0.5); 823 } 824 break; 825 826 case TAG_FOCALLENGTH: 827 // Nice digital cameras actually save the focal length as a function 828 // of how farthey are zoomed in. 829 $this->ImageInfo[TAG_FOCALLENGTH] = $this->ConvertAnyFormat($ValuePtr, $Format); 830 break; 831 832 case TAG_SUBJECT_DISTANCE: 833 // Inidcates the distacne the autofocus camera is focused to. 834 // Tends to be less accurate as distance increases. 835 $this->ImageInfo[TAG_SUBJECT_DISTANCE] = $this->ConvertAnyFormat($ValuePtr, $Format
|