1<?php 2/** 3 * JPEG metadata reader/writer 4 * 5 * @license PHP license 2.0 (http://www.php.net/license/2_02.txt) 6 * @link http://www.zonageek.com/software/php/jpeg/index.php 7 * @author Sebastian Delmont <sdelmont@zonageek.com> 8 * @author Andreas Gohr <andi@splitbrain.org> 9 * @todo Add support for Maker Notes, Extend for GIF and PNG metadata 10 */ 11 12// This class is a modified and enhanced version of the JPEG class by 13// Sebastian Delmont. Original Copyright notice follows: 14// 15// +----------------------------------------------------------------------+ 16// | PHP version 4.0 | 17// +----------------------------------------------------------------------+ 18// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group | 19// +----------------------------------------------------------------------+ 20// | This source file is subject to version 2.0 of the PHP license, | 21// | that is bundled with this package in the file LICENSE, and is | 22// | available at through the world-wide-web at | 23// | http://www.php.net/license/2_02.txt. | 24// | If you did not receive a copy of the PHP license and are unable to | 25// | obtain it through the world-wide-web, please send a note to | 26// | license@php.net so we can mail you a copy immediately. | 27// +----------------------------------------------------------------------+ 28// | Authors: Sebastian Delmont <sdelmont@zonageek.com> | 29// +----------------------------------------------------------------------+ 30 31class JpegMeta 32{ 33 var $_fileName; 34 var $_fp = null; 35 var $_type = 'unknown'; 36 37 var $_markers; 38 var $_info; 39 40 41 /** 42 * Constructor 43 * 44 * @author Sebastian Delmont <sdelmont@zonageek.com> 45 */ 46 function JpegMeta($fileName) 47 { 48 49 $this->_fileName = $fileName; 50 51 $this->_fp = null; 52 $this->_type = 'unknown'; 53 54 unset($this->_info); 55 unset($this->_markers); 56 } 57 58 /** 59 * Returns all gathered info as multidim array 60 * 61 * @author Sebastian Delmont <sdelmont@zonageek.com> 62 */ 63 function & getRawInfo() 64 { 65 $this->_parseAll(); 66 67 if ($this->_markers == null) { 68 return false; 69 } 70 71 return $this->_info; 72 } 73 74 /** 75 * Returns basic image info 76 * 77 * @author Sebastian Delmont <sdelmont@zonageek.com> 78 */ 79 function & getBasicInfo() 80 { 81 $this->_parseAll(); 82 83 $info = array(); 84 85 if ($this->_markers == null) { 86 return false; 87 } 88 89 $info['Name'] = $this->_info['file']['Name']; 90 if (isset($this->_info['file']['Url'])) { 91 $info['Url'] = $this->_info['file']['Url']; 92 $info['NiceSize'] = "???KB"; 93 } 94 else { 95 $info['Size'] = $this->_info['file']['Size']; 96 $info['NiceSize'] = $this->_info['file']['NiceSize']; 97 } 98 99 if (@isset($this->_info['sof']['Format'])) { 100 $info['Format'] = $this->_info['sof']['Format'] . " JPEG"; 101 } 102 else { 103 $info['Format'] = $this->_info['sof']['Format'] . " JPEG"; 104 } 105 106 if (@isset($this->_info['sof']['ColorChannels'])) { 107 $info['ColorMode'] = ($this->_info['sof']['ColorChannels'] > 1) ? "Color" : "B&W"; 108 } 109 110 $info['Width'] = $this->getWidth(); 111 $info['Height'] = $this->getHeight(); 112 $info['DimStr'] = $this->getDimStr(); 113 114 $dates = $this->getDates(); 115 116 $info['DateTime'] = $dates['EarliestTime']; 117 $info['DateTimeStr'] = $dates['EarliestTimeStr']; 118 119 $info['HasThumbnail'] = $this->hasThumbnail(); 120 121 return $info; 122 } 123 124 125 /** 126 * Convinience function to access nearly all available Data 127 * through one function 128 * 129 * @author Andreas Gohr <andi@splitbrain.org> 130 */ 131 function getField($fields) 132 { 133 if(!is_array($fields)) $fields = array($fields); 134 $info = false; 135 foreach($fields as $field){ 136 if(strtolower(substr($field,0,5)) == 'iptc.'){ 137 $info = $this->getIPTCField(substr($field,5)); 138 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 139 $info = $this->getExifField(substr($field,5)); 140 }elseif(strtolower(substr($field,0,5)) == 'file.'){ 141 $info = $this->getFileField(substr($field,5)); 142 }elseif(strtolower(substr($field,0,5)) == 'date.'){ 143 $info = $this->getDateField(substr($field,5)); 144 }elseif(strtolower($field) == 'simple.camera'){ 145 $info = $this->getCamera(); 146 }elseif(strtolower($field) == 'simple.raw'){ 147 return $this->getRawInfo(); 148 }elseif(strtolower($field) == 'simple.title'){ 149 $info = $this->getTitle(); 150 }elseif(strtolower($field) == 'simple.shutterspeed'){ 151 $info = $this->getShutterSpeed(); 152 }else{ 153 $info = $this->getExifField($field); 154 } 155 if($info != false) break; 156 } 157 158 if($info === false) $info = $alt; 159 if(is_array($info)){ 160 if(isset($info['val'])){ 161 $info = $info['val']; 162 }else{ 163 $info = join(', ',$info); 164 } 165 } 166 return trim($info); 167 } 168 169 /** 170 * Convinience function to set nearly all available Data 171 * through one function 172 * 173 * @author Andreas Gohr <andi@splitbrain.org> 174 */ 175 function setField($field, $value) 176 { 177 if(strtolower(substr($field,0,5)) == 'iptc.'){ 178 return $this->setIPTCField(substr($field,5),$value); 179 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 180 return $this->setExifField(substr($field,5),$value); 181 }else{ 182 return $this->setExifField($field,$value); 183 } 184 } 185 186 /** 187 * Convinience function to delete nearly all available Data 188 * through one function 189 * 190 * @author Andreas Gohr <andi@splitbrain.org> 191 */ 192 function deleteField($field) 193 { 194 if(strtolower(substr($field,0,5)) == 'iptc.'){ 195 return $this->deleteIPTCField(substr($field,5)); 196 }elseif(strtolower(substr($field,0,5)) == 'exif.'){ 197 return $this->deleteExifField(substr($field,5)); 198 }else{ 199 return $this->deleteExifField($field); 200 } 201 } 202 203 /** 204 * Return a date field 205 * 206 * @author Andreas Gohr <andi@splitbrain.org> 207 */ 208 function getDateField($field) 209 { 210 if (!isset($this->_info['dates'])) { 211 $this->_info['dates'] = $this->getDates(); 212 } 213 214 if (isset($this->_info['dates'][$field])) { 215 return $this->_info['dates'][$field]; 216 } 217 218 return false; 219 } 220 221 /** 222 * Return a file info field 223 * 224 * @author Andreas Gohr <andi@splitbrain.org> 225 */ 226 function getFileField($field) 227 { 228 if (!isset($this->_info['file'])) { 229 $this->_parseFileInfo(); 230 } 231 232 if (isset($this->_info['file'][$field])) { 233 return $this->_info['file'][$field]; 234 } 235 236 return false; 237 } 238 239 /** 240 * Return the camera info (Maker and Model) 241 * 242 * @author Andreas Gohr <andi@splitbrain.org> 243 * @todo handle makernotes 244 */ 245 function getCamera(){ 246 $make = $this->getField(array('Exif.Make','Exif.TIFFMake')); 247 $model = $this->getField(array('Exif.Model','Exif.TIFFModel')); 248 $cam = trim("$make $model"); 249 if(empty($cam)) return false; 250 return $cam; 251 } 252 253 /** 254 * Return shutter speed as a ratio 255 * 256 * @author Joe Lapp <joe.lapp@pobox.com> 257 */ 258 function getShutterSpeed() 259 { 260 if (!isset($this->_info['exif'])) { 261 $this->_parseMarkerExif(); 262 } 263 if(!isset($this->_info['exif']['ExposureTime'])){ 264 return ''; 265 } 266 267 $field = $this->_info['exif']['ExposureTime']; 268 if($field['den'] == 1) return $field['num']; 269 return $field['num'].'/'.$field['den']; 270 } 271 272 /** 273 * Return an EXIF field 274 * 275 * @author Sebastian Delmont <sdelmont@zonageek.com> 276 */ 277 function getExifField($field) 278 { 279 if (!isset($this->_info['exif'])) { 280 $this->_parseMarkerExif(); 281 } 282 283 if ($this->_markers == null) { 284 return false; 285 } 286 287 if (isset($this->_info['exif'][$field])) { 288 return $this->_info['exif'][$field]; 289 } 290 291 return false; 292 } 293 294 /** 295 * Return an Adobe Field 296 * 297 * @author Sebastian Delmont <sdelmont@zonageek.com> 298 */ 299 function getAdobeField($field) 300 { 301 if (!isset($this->_info['adobe'])) { 302 $this->_parseMarkerAdobe(); 303 } 304 305 if ($this->_markers == null) { 306 return false; 307 } 308 309 if (isset($this->_info['adobe'][$field])) { 310 return $this->_info['adobe'][$field]; 311 } 312 313 return false; 314 } 315 316 /** 317 * Return an IPTC field 318 * 319 * @author Sebastian Delmont <sdelmont@zonageek.com> 320 */ 321 function getIPTCField($field) 322 { 323 if (!isset($this->_info['iptc'])) { 324 $this->_parseMarkerAdobe(); 325 } 326 327 if ($this->_markers == null) { 328 return false; 329 } 330 331 if (isset($this->_info['iptc'][$field])) { 332 return $this->_info['iptc'][$field]; 333 } 334 335 return false; 336 } 337 338 /** 339 * Set an EXIF field 340 * 341 * @author Sebastian Delmont <sdelmont@zonageek.com> 342 */ 343 function setExifField($field, $value) 344 { 345 if (!isset($this->_info['exif'])) { 346 $this->_parseMarkerExif(); 347 } 348 349 if ($this->_markers == null) { 350 return false; 351 } 352 353 if ($this->_info['exif'] == false) { 354 $this->_info['exif'] = array(); 355 } 356 357 $this->_info['exif'][$field] = $value; 358 359 return true; 360 } 361 362 /** 363 * Set an Adobe Field 364 * 365 * @author Sebastian Delmont <sdelmont@zonageek.com> 366 */ 367 function setAdobeField($field, $value) 368 { 369 if (!isset($this->_info['adobe'])) { 370 $this->_parseMarkerAdobe(); 371 } 372 373 if ($this->_markers == null) { 374 return false; 375 } 376 377 if ($this->_info['adobe'] == false) { 378 $this->_info['adobe'] = array(); 379 } 380 381 $this->_info['adobe'][$field] = $value; 382 383 return true; 384 } 385 386 /** 387 * Calculates the multiplier needed to resize the image to the given 388 * dimensions 389 * 390 * @author Andreas Gohr <andi@splitbrain.org> 391 */ 392 function getResizeRatio($maxwidth,$maxheight=0){ 393 if(!$maxheight) $maxheight = $maxwidth; 394 395 $w = $this->getField('File.Width'); 396 $h = $this->getField('File.Height'); 397 398 $ratio = 1; 399 if($w >= $h){ 400 if($w >= $maxwidth){ 401 $ratio = $maxwidth/$w; 402 }elseif($h > $maxheight){ 403 $ratio = $maxheight/$h; 404 } 405 }else{ 406 if($h >= $maxheight){ 407 $ratio = $maxheight/$h; 408 }elseif($w > $maxwidth){ 409 $ratio = $maxwidth/$w; 410 } 411 } 412 return $ratio; 413 } 414 415 416 /** 417 * Set an IPTC field 418 * 419 * @author Sebastian Delmont <sdelmont@zonageek.com> 420 */ 421 function setIPTCField($field, $value) 422 { 423 if (!isset($this->_info['iptc'])) { 424 $this->_parseMarkerAdobe(); 425 } 426 427 if ($this->_markers == null) { 428 return false; 429 } 430 431 if ($this->_info['iptc'] == false) { 432 $this->_info['iptc'] = array(); 433 } 434 435 $this->_info['iptc'][$field] = $value; 436 437 return true; 438 } 439 440 /** 441 * Delete an EXIF field 442 * 443 * @author Sebastian Delmont <sdelmont@zonageek.com> 444 */ 445 function deleteExifField($field) 446 { 447 if (!isset($this->_info['exif'])) { 448 $this->_parseMarkerAdobe(); 449 } 450 451 if ($this->_markers == null) { 452 return false; 453 } 454 455 if ($this->_info['exif'] != false) { 456 unset($this->_info['exif'][$field]); 457 } 458 459 return true; 460 } 461 462 /** 463 * Delete an Adobe field 464 * 465 * @author Sebastian Delmont <sdelmont@zonageek.com> 466 */ 467 function deleteAdobeField($field) 468 { 469 if (!isset($this->_info['adobe'])) { 470 $this->_parseMarkerAdobe(); 471 } 472 473 if ($this->_markers == null) { 474 return false; 475 } 476 477 if ($this->_info['adobe'] != false) { 478 unset($this->_info['adobe'][$field]); 479 } 480 481 return true; 482 } 483 484 /** 485 * Delete an IPTC field 486 * 487 * @author Sebastian Delmont <sdelmont@zonageek.com> 488 */ 489 function deleteIPTCField($field) 490 { 491 if (!isset($this->_info['iptc'])) { 492 $this->_parseMarkerAdobe(); 493 } 494 495 if ($this->_markers == null) { 496 return false; 497 } 498 499 if ($this->_info['iptc'] != false) { 500 unset($this->_info['iptc'][$field]); 501 } 502 503 return true; 504 } 505 506 /** 507 * Get the image's title, tries various fields 508 * 509 * @param int $max maximum number chars (keeps words) 510 * @author Andreas Gohr <andi@splitbrain.org> 511 */ 512 function getTitle($max=80){ 513 $cap = ''; 514 515 // try various fields 516 $cap = $this->getField(array('Iptc.Headline', 517 'Iptc.Caption', 518 'Exif.UserComment', 519 'Exif.TIFFUserComment', 520 'Exif.TIFFImageDescription', 521 'File.Name')); 522 if (empty($cap)) return false; 523 524 if(!$max) return $cap; 525 // Shorten to 80 chars (keeping words) 526 $new = preg_replace('/\n.+$/','',wordwrap($cap, $max)); 527 if($new != $cap) $new .= '...'; 528 529 return $new; 530 } 531 532 /** 533 * Gather various date fields 534 * 535 * @author Sebastian Delmont <sdelmont@zonageek.com> 536 */ 537 function getDates() 538 { 539 $this->_parseAll(); 540 541 if ($this->_markers == null) { 542 return false; 543 } 544 545 $dates = array(); 546 547 $latestTime = 0; 548 $latestTimeSource = ""; 549 $earliestTime = time(); 550 $earliestTimeSource = ""; 551 552 if (@isset($this->_info['exif']['DateTime'])) { 553 $dates['ExifDateTime'] = $this->_info['exif']['DateTime']; 554 555 $aux = $this->_info['exif']['DateTime']; 556 $aux{4} = "-"; 557 $aux{7} = "-"; 558 $t = strtotime($aux); 559 560 if ($t > $latestTime) { 561 $latestTime = $t; 562 $latestTimeSource = "ExifDateTime"; 563 } 564 565 if ($t < $earliestTime) { 566 $earliestTime = $t; 567 $earliestTimeSource = "ExifDateTime"; 568 } 569 } 570 571 if (@isset($this->_info['exif']['DateTimeOriginal'])) { 572 $dates['ExifDateTimeOriginal'] = $this->_info['exif']['DateTime']; 573 574 $aux = $this->_info['exif']['DateTimeOriginal']; 575 $aux{4} = "-"; 576 $aux{7} = "-"; 577 $t = strtotime($aux); 578 579 if ($t > $latestTime) { 580 $latestTime = $t; 581 $latestTimeSource = "ExifDateTimeOriginal"; 582 } 583 584 if ($t < $earliestTime) { 585 $earliestTime = $t; 586 $earliestTimeSource = "ExifDateTimeOriginal"; 587 } 588 } 589 590 if (@isset($this->_info['exif']['DateTimeDigitized'])) { 591 $dates['ExifDateTimeDigitized'] = $this->_info['exif']['DateTime']; 592 593 $aux = $this->_info['exif']['DateTimeDigitized']; 594 $aux{4} = "-"; 595 $aux{7} = "-"; 596 $t = strtotime($aux); 597 598 if ($t > $latestTime) { 599 $latestTime = $t; 600 $latestTimeSource = "ExifDateTimeDigitized"; 601 } 602 603 if ($t < $earliestTime) { 604 $earliestTime = $t; 605 $earliestTimeSource = "ExifDateTimeDigitized"; 606 } 607 } 608 609 if (@isset($this->_info['iptc']['DateCreated'])) { 610 $dates['IPTCDateCreated'] = $this->_info['iptc']['DateCreated']; 611 612 $aux = $this->_info['iptc']['DateCreated']; 613 $aux = substr($aux, 0, 4) . "-" . substr($aux, 4, 2) . "-" . substr($aux, 6, 2); 614 $t = strtotime($aux); 615 616 if ($t > $latestTime) { 617 $latestTime = $t; 618 $latestTimeSource = "IPTCDateCreated"; 619 } 620 621 if ($t < $earliestTime) { 622 $earliestTime = $t; 623 $earliestTimeSource = "IPTCDateCreated"; 624 } 625 } 626 627 if (@isset($this->_info['file']['UnixTime'])) { 628 $dates['FileModified'] = $this->_info['file']['UnixTime']; 629 630 $t = $this->_info['file']['UnixTime']; 631 632 if ($t > $latestTime) { 633 $latestTime = $t; 634 $latestTimeSource = "FileModified"; 635 } 636 637 if ($t < $earliestTime) { 638 $earliestTime = $t; 639 $earliestTimeSource = "FileModified"; 640 } 641 } 642 643 $dates['Time'] = $earliestTime; 644 $dates['TimeSource'] = $earliestTimeSource; 645 $dates['TimeStr'] = date("Y-m-d H:i:s", $earliestTime); 646 $dates['EarliestTime'] = $earliestTime; 647 $dates['EarliestTimeSource'] = $earliestTimeSource; 648 $dates['EarliestTimeStr'] = date("Y-m-d H:i:s", $earliestTime); 649 $dates['LatestTime'] = $latestTime; 650 $dates['LatestTimeSource'] = $latestTimeSource; 651 $dates['LatestTimeStr'] = date("Y-m-d H:i:s", $latestTime); 652 653 return $dates; 654 } 655 656 /** 657 * Get the image width, tries various fields 658 * 659 * @author Sebastian Delmont <sdelmont@zonageek.com> 660 */ 661 function getWidth() 662 { 663 if (!isset($this->_info['sof'])) { 664 $this->_parseMarkerSOF(); 665 } 666 667 if ($this->_markers == null) { 668 return false; 669 } 670 671 if (isset($this->_info['sof']['ImageWidth'])) { 672 return $this->_info['sof']['ImageWidth']; 673 } 674 675 if (!isset($this->_info['exif'])) { 676 $this->_parseMarkerExif(); 677 } 678 679 if (isset($this->_info['exif']['PixelXDimension'])) { 680 return $this->_info['exif']['PixelXDimension']; 681 } 682 683 return false; 684 } 685 686 /** 687 * Get the image height, tries various fields 688 * 689 * @author Sebastian Delmont <sdelmont@zonageek.com> 690 */ 691 function getHeight() 692 { 693 if (!isset($this->_info['sof'])) { 694 $this->_parseMarkerSOF(); 695 } 696 697 if ($this->_markers == null) { 698 return false; 699 } 700 701 if (isset($this->_info['sof']['ImageHeight'])) { 702 return $this->_info['sof']['ImageHeight']; 703 } 704 705 if (!isset($this->_info['exif'])) { 706 $this->_parseMarkerExif(); 707 } 708 709 if (isset($this->_info['exif']['PixelYDimension'])) { 710 return $this->_info['exif']['PixelYDimension']; 711 } 712 713 return false; 714 } 715 716 /** 717 * Get an dimension string for use in img tag 718 * 719 * @author Sebastian Delmont <sdelmont@zonageek.com> 720 */ 721 function getDimStr() 722 { 723 if ($this->_markers == null) { 724 return false; 725 } 726 727 $w = $this->getWidth(); 728 $h = $this->getHeight(); 729 730 return "width='" . $w . "' height='" . $h . "'"; 731 } 732 733 /** 734 * Checks for an embedded thumbnail 735 * 736 * @author Sebastian Delmont <sdelmont@zonageek.com> 737 */ 738 function hasThumbnail($which = 'any') 739 { 740 if (($which == 'any') || ($which == 'exif')) { 741 if (!isset($this->_info['exif'])) { 742 $this->_parseMarkerExif(); 743 } 744 745 if ($this->_markers == null) { 746 return false; 747 } 748 749 if (isset($this->_info['exif']) && is_array($this->_info['exif'])) { 750 if (isset($this->_info['exif']['JFIFThumbnail'])) { 751 return 'exif'; 752 } 753 } 754 } 755 756 if ($which == 'adobe') { 757 if (!isset($this->_info['adobe'])) { 758 $this->_parseMarkerAdobe(); 759 } 760 761 if ($this->_markers == null) { 762 return false; 763 } 764 765 if (isset($this->_info['adobe']) && is_array($this->_info['adobe'])) { 766 if (isset($this->_info['adobe']['ThumbnailData'])) { 767 return 'exif'; 768 } 769 } 770 } 771 772 return false; 773 } 774 775 /** 776 * Send embedded thumbnail to browser 777 * 778 * @author Sebastian Delmont <sdelmont@zonageek.com> 779 */ 780 function sendThumbnail($which = 'any') 781 { 782 $data = null; 783 784 if (($which == 'any') || ($which == 'exif')) { 785 if (!isset($this->_info['exif'])) { 786 $this->_parseMarkerExif(); 787 } 788 789 if ($this->_markers == null) { 790 return false; 791 } 792 793 if (isset($this->_info['exif']) && is_array($this->_info['exif'])) { 794 if (isset($this->_info['exif']['JFIFThumbnail'])) { 795 $data =& $this->_info['exif']['JFIFThumbnail']; 796 } 797 } 798 } 799 800 if (($which == 'adobe') || ($data == null)){ 801 if (!isset($this->_info['adobe'])) { 802 $this->_parseMarkerAdobe(); 803 } 804 805 if ($this->_markers == null) { 806 return false; 807 } 808 809 if (isset($this->_info['adobe']) && is_array($this->_info['adobe'])) { 810 if (isset($this->_info['adobe']['ThumbnailData'])) { 811 $data =& $this->_info['adobe']['ThumbnailData']; 812 } 813 } 814 } 815 816 if ($data != null) { 817 header("Content-type: image/jpeg"); 818 echo $data; 819 return true; 820 } 821 822 return false; 823 } 824 825 /** 826 * Save changed Metadata 827 * 828 * @author Sebastian Delmont <sdelmont@zonageek.com> 829 * @author Andreas Gohr <andi@splitbrain.org> 830 */ 831 function save($fileName = "") { 832 if ($fileName == "") { 833 $tmpName = tempnam(dirname($this->_fileName),'_metatemp_'); 834 $this->_writeJPEG($tmpName); 835 if (@file_exists($tmpName)) { 836 return rename($tmpName, $this->_fileName); 837 } 838 } else { 839 return $this->_writeJPEG($fileName); 840 } 841 return false; 842 } 843 844 /*************************************************************/ 845 /* PRIVATE FUNCTIONS (Internal Use Only!) */ 846 /*************************************************************/ 847 848 /*************************************************************/ 849 function _dispose() 850 { 851 $this->_fileName = $fileName; 852 853 $this->_fp = null; 854 $this->_type = 'unknown'; 855 856 unset($this->_markers); 857 unset($this->_info); 858 } 859 860 /*************************************************************/ 861 function _readJPEG() 862 { 863 unset($this->_markers); 864 unset($this->_info); 865 $this->_markers = array(); 866 $this->_info = array(); 867 868 $this->_fp = @fopen($this->_fileName, 'rb'); 869 if ($this->_fp) { 870 if (file_exists($this->_fileName)) { 871 $this->_type = 'file'; 872 } 873 else { 874 $this->_type = 'url'; 875 } 876 } 877 else { 878 $this->_fp = null; 879 return false; // ERROR: Can't open file 880 } 881 882 // Check for the JPEG signature 883 $c1 = ord(fgetc($this->_fp)); 884 $c2 = ord(fgetc($this->_fp)); 885 886 if ($c1 != 0xFF || $c2 != 0xD8) { // (0xFF + SOI) 887 $this->_markers = null; 888 return false; // ERROR: File is not a JPEG 889 } 890 891 $count = 0; 892 893 $done = false; 894 $ok = true; 895 896 while (!$done) { 897 $capture = false; 898 899 // First, skip any non 0xFF bytes 900 $discarded = 0; 901 $c = ord(fgetc($this->_fp)); 902 while (!feof($this->_fp) && ($c != 0xFF)) { 903 $discarded++; 904 $c = ord(fgetc($this->_fp)); 905 } 906 // Then skip all 0xFF until the marker byte 907 do { 908 $marker = ord(fgetc($this->_fp)); 909 } while (!feof($this->_fp) && ($marker == 0xFF)); 910 911 if (feof($this->_fp)) { 912 return false; // ERROR: Unexpected EOF 913 } 914 if ($discarded != 0) { 915 return false; // ERROR: Extraneous data 916 } 917 918 $length = ord(fgetc($this->_fp)) * 256 + ord(fgetc($this->_fp)); 919 if (feof($this->_fp)) { 920 return false; // ERROR: Unexpected EOF 921 } 922 if ($length < 2) { 923 return false; // ERROR: Extraneous data 924 } 925 $length = $length - 2; // The length we got counts itself 926 927 switch ($marker) { 928 case 0xC0: // SOF0 929 case 0xC1: // SOF1 930 case 0xC2: // SOF2 931 case 0xC9: // SOF9 932 case 0xE0: // APP0: JFIF data 933 case 0xE1: // APP1: EXIF data 934 case 0xED: // APP13: IPTC / Photoshop data 935 $capture = true; 936 break; 937 case 0xDA: // SOS: Start of scan... the image itself and the last block on the file 938 $capture = false; 939 $length = -1; // This field has no length... it includes all data until EOF 940 $done = true; 941 break; 942 default: 943 $capture = true;//false; 944 break; 945 } 946 947 $this->_markers[$count] = array(); 948 $this->_markers[$count]['marker'] = $marker; 949 $this->_markers[$count]['length'] = $length; 950 951 if ($capture) { 952 $this->_markers[$count]['data'] =& fread($this->_fp, $length); 953 } 954 elseif (!$done) { 955 $result = @fseek($this->_fp, $length, SEEK_CUR); 956 // fseek doesn't seem to like HTTP 'files', but fgetc has no problem 957 if (!($result === 0)) { 958 for ($i = 0; $i < $length; $i++) { 959 fgetc($this->_fp); 960 } 961 } 962 } 963 $count++; 964 } 965 966 if ($this->_fp) { 967 fclose($this->_fp); 968 $this->_fp = null; 969 } 970 971 return $ok; 972 } 973 974 /*************************************************************/ 975 function _parseAll() 976 { 977 if (!isset($this->_markers)) { 978 $this->_readJPEG(); 979 } 980 981 if ($this->_markers == null) { 982 return false; 983 } 984 985 if (!isset($this->_info['jfif'])) { 986 $this->_parseMarkerJFIF(); 987 } 988 if (!isset($this->_info['jpeg'])) { 989 $this->_parseMarkerSOF(); 990 } 991 if (!isset($this->_info['exif'])) { 992 $this->_parseMarkerExif(); 993 } 994 if (!isset($this->_info['adobe'])) { 995 $this->_parseMarkerAdobe(); 996 } 997 if (!isset($this->_info['file'])) { 998 $this->_parseFileInfo(); 999 } 1000 } 1001 1002 /*************************************************************/ 1003 function _writeJPEG($outputName) 1004 { 1005 $this->_parseAll(); 1006 1007 $wroteEXIF = false; 1008 $wroteAdobe = false; 1009 1010 $this->_fp = @fopen($this->_fileName, 'r'); 1011 if ($this->_fp) { 1012 if (file_exists($this->_fileName)) { 1013 $this->_type = 'file'; 1014 } 1015 else { 1016 $this->_type = 'url'; 1017 } 1018 } 1019 else { 1020 $this->_fp = null; 1021 return false; // ERROR: Can't open file 1022 } 1023 1024 $this->_fpout = fopen($outputName, 'wb'); 1025 if ($this->_fpout) { 1026 } 1027 else { 1028 $this->_fpout = null; 1029 fclose($this->_fp); 1030 $this->_fp = null; 1031 return false; // ERROR: Can't open output file 1032 } 1033 1034 // Check for the JPEG signature 1035 $c1 = ord(fgetc($this->_fp)); 1036 $c2 = ord(fgetc($this->_fp)); 1037 1038 if ($c1 != 0xFF || $c2 != 0xD8) { // (0xFF + SOI) 1039 return false; // ERROR: File is not a JPEG 1040 } 1041 1042 fputs($this->_fpout, chr(0xFF), 1); 1043 fputs($this->_fpout, chr(0xD8), 1); // (0xFF + SOI) 1044 1045 $count = 0; 1046 1047 $done = false; 1048 $ok = true; 1049 1050 while (!$done) { 1051 // First, skip any non 0xFF bytes 1052 $discarded = 0; 1053 $c = ord(fgetc($this->_fp)); 1054 while (!feof($this->_fp) && ($c != 0xFF)) { 1055 $discarded++; 1056 $c = ord(fgetc($this->_fp)); 1057 } 1058 // Then skip all 0xFF until the marker byte 1059 do { 1060 $marker = ord(fgetc($this->_fp)); 1061 } while (!feof($this->_fp) && ($marker == 0xFF)); 1062 1063 if (feof($this->_fp)) { 1064 $ok = false; 1065 break; // ERROR: Unexpected EOF 1066 } 1067 if ($discarded != 0) { 1068 $ok = false; 1069 break; // ERROR: Extraneous data 1070 } 1071 1072 $length = ord(fgetc($this->_fp)) * 256 + ord(fgetc($this->_fp)); 1073 if (feof($this->_fp)) { 1074 $ok = false; 1075 break; // ERROR: Unexpected EOF 1076 } 1077 if ($length < 2) { 1078 $ok = false; 1079 break; // ERROR: Extraneous data 1080 } 1081 $length = $length - 2; // The length we got counts itself 1082 1083 unset($data); 1084 if ($marker == 0xE1) { // APP1: EXIF data 1085 $data =& $this->_createMarkerEXIF(); 1086 $wroteEXIF = true; 1087 } 1088 elseif ($marker == 0xED) { // APP13: IPTC / Photoshop data 1089 $data =& $this->_createMarkerAdobe(); 1090 $wroteAdobe = true; 1091 } 1092 elseif ($marker == 0xDA) { // SOS: Start of scan... the image itself and the last block on the file 1093 $done = true; 1094 } 1095 1096 if (!$wroteEXIF && (($marker < 0xE0) || ($marker > 0xEF))) { 1097 if (isset($this->_info['exif']) && is_array($this->_info['exif'])) { 1098 $exif =& $this->_createMarkerEXIF(); 1099 $this->_writeJPEGMarker(0xE1, strlen($exif), $exif, 0); 1100 unset($exif); 1101 } 1102 $wroteEXIF = true; 1103 } 1104 1105 if (!$wroteAdobe && (($marker < 0xE0) || ($marker > 0xEF))) { 1106 if ((isset($this->_info['adobe']) && is_array($this->_info['adobe'])) 1107 || (isset($this->_info['iptc']) && is_array($this->_info['iptc']))) { 1108 $adobe =& $this->_createMarkerAdobe(); 1109 $this->_writeJPEGMarker(0xED, strlen($adobe), $adobe, 0); 1110 unset($adobe); 1111 } 1112 $wroteAdobe = true; 1113 } 1114 1115 $origLength = $length; 1116 if (isset($data)) { 1117 $length = strlen($data); 1118 } 1119 1120 if ($marker != -1) { 1121 $this->_writeJPEGMarker($marker, $length, $data, $origLength); 1122 } 1123 } 1124 1125 if ($this->_fp) { 1126 fclose($this->_fp); 1127 $this->_fp = null; 1128 } 1129 1130 if ($this->_fpout) { 1131 fclose($this->_fpout); 1132 $this->_fpout = null; 1133 } 1134 1135 return $ok; 1136 } 1137 1138 /*************************************************************/ 1139 function _writeJPEGMarker($marker, $length, &$data, $origLength) 1140 { 1141 if ($length <= 0) { 1142 return false; 1143 } 1144 1145 fputs($this->_fpout, chr(0xFF), 1); 1146 fputs($this->_fpout, chr($marker), 1); 1147 fputs($this->_fpout, chr((($length + 2) & 0x0000FF00) >> 8), 1); 1148 fputs($this->_fpout, chr((($length + 2) & 0x000000FF) >> 0), 1); 1149 1150 if (isset($data)) { 1151 // Copy the generated data 1152 fputs($this->_fpout, $data, $length); 1153 1154 if ($origLength > 0) { // Skip the original data 1155 $result = @fseek($this->_fp, $origLength, SEEK_CUR); 1156 // fseek doesn't seem to like HTTP 'files', but fgetc has no problem 1157 if ($result != 0) { 1158 for ($i = 0; $i < $origLength; $i++) { 1159 fgetc($this->_fp); 1160 } 1161 } 1162 } 1163 } 1164 else { 1165 if ($marker == 0xDA) { // Copy until EOF 1166 while (!feof($this->_fp)) { 1167 $data =& fread($this->_fp, 1024 * 16); 1168 fputs($this->_fpout, $data, strlen($data)); 1169 } 1170 } 1171 else { // Copy only $length bytes 1172 $data =& fread($this->_fp, $length); 1173 fputs($this->_fpout, $data, $length); 1174 } 1175 } 1176 1177 return true; 1178 } 1179 1180 /** 1181 * Gets basic info from the file - should work with non-JPEGs 1182 * 1183 * @author Sebastian Delmont <sdelmont@zonageek.com> 1184 * @author Andreas Gohr <andi@splitbrain.org> 1185 */ 1186 function _parseFileInfo() 1187 { 1188 if (file_exists($this->_fileName)) { 1189 $this->_info['file'] = array(); 1190 $this->_info['file']['Name'] = basename($this->_fileName); 1191 $this->_info['file']['Path'] = realpath($this->_fileName); 1192 $this->_info['file']['Size'] = filesize($this->_fileName); 1193 if ($this->_info['file']['Size'] < 1024) { 1194 $this->_info['file']['NiceSize'] = $this->_info['file']['Size'] . 'B'; 1195 } 1196 elseif ($this->_info['file']['Size'] < (1024 * 1024)) { 1197 $this->_info['file']['NiceSize'] = round($this->_info['file']['Size'] / 1024) . 'KB'; 1198 } 1199 elseif ($this->_info['file']['Size'] < (1024 * 1024 * 1024)) { 1200 $this->_info['file']['NiceSize'] = round($this->_info['file']['Size'] / 1024) . 'MB'; 1201 } 1202 else { 1203 $this->_info['file']['NiceSize'] = $this->_info['file']['Size'] . 'B'; 1204 } 1205 $this->_info['file']['UnixTime'] = filemtime($this->_fileName); 1206 1207 // get image size directly from file 1208 $size = getimagesize($this->_fileName); 1209 $this->_info['file']['Width'] = $size[0]; 1210 $this->_info['file']['Height'] = $size[1]; 1211 // set mime types and formats 1212 // http://www.php.net/manual/en/function.getimagesize.php 1213 // http://www.php.net/manual/en/function.image-type-to-mime-type.php 1214 switch ($size[2]){ 1215 case 1: 1216 $this->_info['file']['Mime'] = 'image/gif'; 1217 $this->_info['file']['Format'] = 'GIF'; 1218 break; 1219 case 2: 1220 $this->_info['file']['Mime'] = 'image/jpeg'; 1221 $this->_info['file']['Format'] = 'JPEG'; 1222 break; 1223 case 3: 1224 $this->_info['file']['Mime'] = 'image/png'; 1225 $this->_info['file']['Format'] = 'PNG'; 1226 break; 1227 case 4: 1228 $this->_info['file']['Mime'] = 'application/x-shockwave-flash'; 1229 $this->_info['file']['Format'] = 'SWF'; 1230 break; 1231 case 5: 1232 $this->_info['file']['Mime'] = 'image/psd'; 1233 $this->_info['file']['Format'] = 'PSD'; 1234 break; 1235 case 6: 1236 $this->_info['file']['Mime'] = 'image/bmp'; 1237 $this->_info['file']['Format'] = 'BMP'; 1238 break; 1239 case 7: 1240 $this->_info['file']['Mime'] = 'image/tiff'; 1241 $this->_info['file']['Format'] = 'TIFF (Intel)'; 1242 break; 1243 case 8: 1244 $this->_info['file']['Mime'] = 'image/tiff'; 1245 $this->_info['file']['Format'] = 'TIFF (Motorola)'; 1246 break; 1247 case 9: 1248 $this->_info['file']['Mime'] = 'application/octet-stream'; 1249 $this->_info['file']['Format'] = 'JPC'; 1250 break; 1251 case 10: 1252 $this->_info['file']['Mime'] = 'image/jp2'; 1253 $this->_info['file']['Format'] = 'JP2'; 1254 break; 1255 case 11: 1256 $this->_info['file']['Mime'] = 'application/octet-stream'; 1257 $this->_info['file']['Format'] = 'JPX'; 1258 break; 1259 case 12: 1260 $this->_info['file']['Mime'] = 'application/octet-stream'; 1261 $this->_info['file']['Format'] = 'JB2'; 1262 break; 1263 case 13: 1264 $this->_info['file']['Mime'] = 'application/x-shockwave-flash'; 1265 $this->_info['file']['Format'] = 'SWC'; 1266 break; 1267 case 14: 1268 $this->_info['file']['Mime'] = 'image/iff'; 1269 $this->_info['file']['Format'] = 'IFF'; 1270 break; 1271 case 15: 1272 $this->_info['file']['Mime'] = 'image/vnd.wap.wbmp'; 1273 $this->_info['file']['Format'] = 'WBMP'; 1274 break; 1275 case 16: 1276 $this->_info['file']['Mime'] = 'image/xbm'; 1277 $this->_info['file']['Format'] = 'XBM'; 1278 break; 1279 default: 1280 $this->_info['file']['Mime'] = 'image/unknown'; 1281 } 1282 } 1283 else { 1284 $this->_info['file'] = array(); 1285 $this->_info['file']['Name'] = basename($this->_fileName); 1286 $this->_info['file']['Url'] = $this->_fileName; 1287 } 1288 1289 return true; 1290 } 1291 1292 /*************************************************************/ 1293 function _parseMarkerJFIF() 1294 { 1295 if (!isset($this->_markers)) { 1296 $this->_readJPEG(); 1297 } 1298 1299 if ($this->_markers == null) { 1300 return false; 1301 } 1302 1303 $data = null; 1304 $count = count($this->_markers); 1305 for ($i = 0; $i < $count; $i++) { 1306 if ($this->_markers[$i]['marker'] == 0xE0) { 1307 $signature = $this->_getFixedString($this->_markers[$i]['data'], 0, 4); 1308 if ($signature == 'JFIF') { 1309 $data =& $this->_markers[$i]['data']; 1310 break; 1311 } 1312 } 1313 } 1314 1315 if ($data == null) { 1316 $this->_info['jfif'] = false; 1317 return false; 1318 } 1319 1320 $pos = 0; 1321 $this->_info['jfif'] = array(); 1322 1323 1324 $vmaj = $this->_getByte($data, 5); 1325 $vmin = $this->_getByte($data, 6); 1326 1327 $this->_info['jfif']['Version'] = sprintf('%d.%02d', $vmaj, $vmin); 1328 1329 $units = $this->_getByte($data, 7); 1330 switch ($units) { 1331 case 0: 1332 $this->_info['jfif']['Units'] = 'pixels'; 1333 break; 1334 case 1: 1335 $this->_info['jfif']['Units'] = 'dpi'; 1336 break; 1337 case 2: 1338 $this->_info['jfif']['Units'] = 'dpcm'; 1339 break; 1340 default: 1341 $this->_info['jfif']['Units'] = 'unknown'; 1342 break; 1343 } 1344 1345 $xdens = $this->_getShort($data, 8); 1346 $ydens = $this->_getShort($data, 10); 1347 1348 $this->_info['jfif']['XDensity'] = $xdens; 1349 $this->_info['jfif']['YDensity'] = $ydens; 1350 1351 $thumbx = $this->_getByte($data, 12); 1352 $thumby = $this->_getByte($data, 13); 1353 1354 $this->_info['jfif']['ThumbnailWidth'] = $thumbx; 1355 $this->_info['jfif']['ThumbnailHeight'] = $thumby; 1356 1357 return true; 1358 } 1359 1360 /*************************************************************/ 1361 function _parseMarkerSOF() 1362 { 1363 if (!isset($this->_markers)) { 1364 $this->_readJPEG(); 1365 } 1366 1367 if ($this->_markers == null) { 1368 return false; 1369 } 1370 1371 $data = null; 1372 $count = count($this->_markers); 1373 for ($i = 0; $i < $count; $i++) { 1374 switch ($this->_markers[$i]['marker']) { 1375 case 0xC0: // SOF0 1376 case 0xC1: // SOF1 1377 case 0xC2: // SOF2 1378 case 0xC9: // SOF9 1379 $data =& $this->_markers[$i]['data']; 1380 $marker = $this->_markers[$i]['marker']; 1381 break; 1382 } 1383 } 1384 1385 if ($data == null) { 1386 $this->_info['sof'] = false; 1387 return false; 1388 } 1389 1390 $pos = 0; 1391 $this->_info['sof'] = array(); 1392 1393 1394 switch ($marker) { 1395 case 0xC0: // SOF0 1396 $format = 'Baseline'; 1397 break; 1398 case 0xC1: // SOF1 1399 $format = 'Progessive'; 1400 break; 1401 case 0xC2: // SOF2 1402 $format = 'Non-baseline'; 1403 break; 1404 case 0xC9: // SOF9 1405 $format = 'Arithmetic'; 1406 break; 1407 default: 1408 return false; 1409 break; 1410 } 1411 1412 1413 $this->_info['sof']['Format'] = $format; 1414 1415 $this->_info['sof']['SamplePrecision'] = $this->_getByte($data, $pos + 0); 1416 $this->_info['sof']['ImageHeight'] = $this->_getShort($data, $pos + 1); 1417 $this->_info['sof']['ImageWidth'] = $this->_getShort($data, $pos + 3); 1418 $this->_info['sof']['ColorChannels'] = $this->_getByte($data, $pos + 5); 1419 1420 return true; 1421 } 1422 1423 /*************************************************************/ 1424 function _parseMarkerExif() 1425 { 1426 if (!isset($this->_markers)) { 1427 $this->_readJPEG(); 1428 } 1429 1430 if ($this->_markers == null) { 1431 return false; 1432 } 1433 1434 $data = null; 1435 $count = count($this->_markers); 1436 for ($i = 0; $i < $count; $i++) { 1437 if ($this->_markers[$i]['marker'] == 0xE1) { 1438 $signature = $this->_getFixedString($this->_markers[$i]['data'], 0, 6); 1439 if ($signature == "Exif\0\0") { 1440 $data =& $this->_markers[$i]['data']; 1441 break; 1442 } 1443 } 1444 } 1445 1446 if ($data == null) { 1447 $this->_info['exif'] = false; 1448 return false; 1449 } 1450 $pos = 6; 1451 $this->_info['exif'] = array(); 1452 1453 // We don't increment $pos after this because Exif uses offsets relative to this point 1454 1455 $byteAlign = $this->_getShort($data, $pos + 0); 1456 1457 if ($byteAlign == 0x4949) { // "II" 1458 $isBigEndian = false; 1459 } 1460 elseif ($byteAlign == 0x4D4D) { // "MM" 1461 $isBigEndian = true; 1462 } 1463 else { 1464 return false; // Unexpected data 1465 } 1466 1467 $alignCheck = $this->_getShort($data, $pos + 2, $isBigEndian); 1468 if ($alignCheck != 0x002A) // That's the expected value 1469 return false; // Unexpected data 1470 1471 if ($isBigEndian) { 1472 $this->_info['exif']['ByteAlign'] = "Big Endian"; 1473 } 1474 else { 1475 $this->_info['exif']['ByteAlign'] = "Little Endian"; 1476 } 1477 1478 $offsetIFD0 = $this->_getLong($data, $pos + 4, $isBigEndian); 1479 if ($offsetIFD0 < 8) 1480 return false; // Unexpected data 1481 1482 $offsetIFD1 = $this->_readIFD($data, $pos, $offsetIFD0, $isBigEndian, 'ifd0'); 1483 if ($offsetIFD1 != 0) 1484 $this->_readIFD($data, $pos, $offsetIFD1, $isBigEndian, 'ifd1'); 1485 1486 return true; 1487 } 1488 1489 /*************************************************************/ 1490 function _readIFD($data, $base, $offset, $isBigEndian, $mode) 1491 { 1492 $EXIFTags = $this->_exifTagNames($mode); 1493 1494 $numEntries = $this->_getShort($data, $base + $offset, $isBigEndian); 1495 $offset += 2; 1496 1497 $exifTIFFOffset = 0; 1498 $exifTIFFLength = 0; 1499 $exifThumbnailOffset = 0; 1500 $exifThumbnailLength = 0; 1501 1502 for ($i = 0; $i < $numEntries; $i++) { 1503 $tag = $this->_getShort($data, $base + $offset, $isBigEndian); 1504 $offset += 2; 1505 $type = $this->_getShort($data, $base + $offset, $isBigEndian); 1506 $offset += 2; 1507 $count = $this->_getLong($data, $base + $offset, $isBigEndian); 1508 $offset += 4; 1509 1510 if (($type < 1) || ($type > 12)) 1511 return false; // Unexpected Type 1512 1513 $typeLengths = array( -1, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 ); 1514 1515 $dataLength = $typeLengths[$type] * $count; 1516 if ($dataLength > 4) { 1517 $dataOffset = $this->_getLong($data, $base + $offset, $isBigEndian); 1518 $rawValue = $this->_getFixedString($data, $base + $dataOffset, $dataLength); 1519 } 1520 else { 1521 $rawValue = $this->_getFixedString($data, $base + $offset, $dataLength); 1522 } 1523 $offset += 4; 1524 1525 switch ($type) { 1526 case 1: // UBYTE 1527 if ($count == 1) { 1528 $value = $this->_getByte($rawValue, 0); 1529 } 1530 else { 1531 $value = array(); 1532 for ($j = 0; $j < $count; $j++) 1533 $value[$j] = $this->_getByte($rawValue, $j); 1534 } 1535 break; 1536 case 2: // ASCII 1537 $value = $rawValue; 1538 break; 1539 case 3: // USHORT 1540 if ($count == 1) { 1541 $value = $this->_getShort($rawValue, 0, $isBigEndian); 1542 } 1543 else { 1544 $value = array(); 1545 for ($j = 0; $j < $count; $j++) 1546 $value[$j] = $this->_getShort($rawValue, $j * 2, $isBigEndian); 1547 } 1548 break; 1549 case 4: // ULONG 1550 if ($count == 1) { 1551 $value = $this->_getLong($rawValue, 0, $isBigEndian); 1552 } 1553 else { 1554 $value = array(); 1555 for ($j = 0; $j < $count; $j++) 1556 $value[$j] = $this->_getLong($rawValue, $j * 4, $isBigEndian); 1557 } 1558 break; 1559 case 5: // URATIONAL 1560 if ($count == 1) { 1561 $a = $this->_getLong($rawValue, 0, $isBigEndian); 1562 $b = $this->_getLong($rawValue, 4, $isBigEndian); 1563 $value = array(); 1564 $value['val'] = 0; 1565 $value['num'] = $a; 1566 $value['den'] = $b; 1567 if (($a != 0) && ($b != 0)) { 1568 $value['val'] = $a / $b; 1569 } 1570 } 1571 else { 1572 $value = array(); 1573 for ($j = 0; $j < $count; $j++) { 1574 $a = $this->_getLong($rawValue, $j * 8, $isBigEndian); 1575 $b = $this->_getLong($rawValue, ($j * 8) + 4, $isBigEndian); 1576 $value = array(); 1577 $value[$j]['val'] = 0; 1578 $value[$j]['num'] = $a; 1579 $value[$j]['den'] = $b; 1580 if (($a != 0) && ($b != 0)) 1581 $value[$j]['val'] = $a / $b; 1582 } 1583 } 1584 break; 1585 case 6: // SBYTE 1586 if ($count == 1) { 1587 $value = $this->_getByte($rawValue, 0); 1588 } 1589 else { 1590 $value = array(); 1591 for ($j = 0; $j < $count; $j++) 1592 $value[$j] = $this->_getByte($rawValue, $j); 1593 } 1594 break; 1595 case 7: // UNDEFINED 1596 $value = $rawValue; 1597 break; 1598 case 8: // SSHORT 1599 if ($count == 1) { 1600 $value = $this->_getShort($rawValue, 0, $isBigEndian); 1601 } 1602 else { 1603 $value = array(); 1604 for ($j = 0; $j < $count; $j++) 1605 $value[$j] = $this->_getShort($rawValue, $j * 2, $isBigEndian); 1606 } 1607 break; 1608 case 9: // SLONG 1609 if ($count == 1) { 1610 $value = $this->_getLong($rawValue, 0, $isBigEndian); 1611 } 1612 else { 1613 $value = array(); 1614 for ($j = 0; $j < $count; $j++) 1615 $value[$j] = $this->_getLong($rawValue, $j * 4, $isBigEndian); 1616 } 1617 break; 1618 case 10: // SRATIONAL 1619 if ($count == 1) { 1620 $a = $this->_getLong($rawValue, 0, $isBigEndian); 1621 $b = $this->_getLong($rawValue, 4, $isBigEndian); 1622 $value = array(); 1623 $value['val'] = 0; 1624 $value['num'] = $a; 1625 $value['den'] = $b; 1626 if (($a != 0) && ($b != 0)) 1627 $value['val'] = $a / $b; 1628 } 1629 else { 1630 $value = array(); 1631 for ($j = 0; $j < $count; $j++) { 1632 $a = $this->_getLong($rawValue, $j * 8, $isBigEndian); 1633 $b = $this->_getLong($rawValue, ($j * 8) + 4, $isBigEndian); 1634 $value = array(); 1635 $value[$j]['val'] = 0; 1636 $value[$j]['num'] = $a; 1637 $value[$j]['den'] = $b; 1638 if (($a != 0) && ($b != 0)) 1639 $value[$j]['val'] = $a / $b; 1640 } 1641 } 1642 break; 1643 case 11: // FLOAT 1644 $value = $rawValue; 1645 break; 1646 1647 case 12: // DFLOAT 1648 $value = $rawValue; 1649 break; 1650 default: 1651 return false; // Unexpected Type 1652 } 1653 1654 $tagName = ''; 1655 if (($mode == 'ifd0') && ($tag == 0x8769)) { // ExifIFDOffset 1656 $this->_readIFD($data, $base, $value, $isBigEndian, 'exif'); 1657 } 1658 elseif (($mode == 'ifd0') && ($tag == 0x8825)) { // GPSIFDOffset 1659 $this->_readIFD($data, $base, $value, $isBigEndian, 'gps'); 1660 } 1661 elseif (($mode == 'ifd1') && ($tag == 0x0111)) { // TIFFStripOffsets 1662 $exifTIFFOffset = $value; 1663 } 1664 elseif (($mode == 'ifd1') && ($tag == 0x0117)) { // TIFFStripByteCounts 1665 $exifTIFFLength = $value; 1666 } 1667 elseif (($mode == 'ifd1') && ($tag == 0x0201)) { // TIFFJFIFOffset 1668 $exifThumbnailOffset = $value; 1669 } 1670 elseif (($mode == 'ifd1') && ($tag == 0x0202)) { // TIFFJFIFLength 1671 $exifThumbnailLength = $value; 1672 } 1673 elseif (($mode == 'exif') && ($tag == 0xA005)) { // InteropIFDOffset 1674 $this->_readIFD($data, $base, $value, $isBigEndian, 'interop'); 1675 } 1676 // elseif (($mode == 'exif') && ($tag == 0x927C)) { // MakerNote 1677 // } 1678 else { 1679 if (isset($EXIFTags[$tag])) { 1680 $tagName = $EXIFTags[$tag]; 1681 if (isset($this->_info['exif'][$tagName])) { 1682 if (!is_array($this->_info['exif'][$tagName])) { 1683 $aux = array(); 1684 $aux[0] = $this->_info['exif'][$tagName]; 1685 $this->_info['exif'][$tagName] = $aux; 1686 } 1687 1688 $this->_info['exif'][$tagName][count($this->_info['exif'][$tagName])] = $value; 1689 } 1690 else { 1691 $this->_info['exif'][$tagName] = $value; 1692 } 1693 } 1694 else { 1695#echo sprintf("<h1>Unknown tag %02x (t: %d l: %d) %s in %s</h1>", $tag, $type, $count, $mode, $this->_fileName); 1696 // Unknown Tags will be ignored!!! 1697 // That's because the tag might be a pointer (like the Exif tag) 1698 // and saving it without saving the data it points to might 1699 // create an invalid file. 1700 } 1701 } 1702 } 1703 1704 if (($exifThumbnailOffset > 0) && ($exifThumbnailLength > 0)) { 1705 $this->_info['exif']['JFIFThumbnail'] = $this->_getFixedString($data, $base + $exifThumbnailOffset, $exifThumbnailLength); 1706 } 1707 1708 if (($exifTIFFOffset > 0) && ($exifTIFFLength > 0)) { 1709 $this->_info['exif']['TIFFStrips'] = $this->_getFixedString($data, $base + $exifTIFFOffset, $exifTIFFLength); 1710 } 1711 1712 $nextOffset = $this->_getLong($data, $base + $offset, $isBigEndian); 1713 return $nextOffset; 1714 } 1715 1716 /*************************************************************/ 1717 function & _createMarkerExif() 1718 { 1719 $data = null; 1720 $count = count($this->_markers); 1721 for ($i = 0; $i < $count; $i++) { 1722 if ($this->_markers[$i]['marker'] == 0xE1) { 1723 $signature = $this->_getFixedString($this->_markers[$i]['data'], 0, 6); 1724 if ($signature == "Exif\0\0") { 1725 $data =& $this->_markers[$i]['data']; 1726 break; 1727 } 1728 } 1729 } 1730 1731 if (!isset($this->_info['exif'])) { 1732 return false; 1733 } 1734 1735 $data = "Exif\0\0"; 1736 $pos = 6; 1737 $offsetBase = 6; 1738 1739 if (isset($this->_info['exif']['ByteAlign']) && ($this->_info['exif']['ByteAlign'] == "Big Endian")) { 1740 $isBigEndian = true; 1741 $aux = "MM"; 1742 $pos = $this->_putString($data, $pos, $aux); 1743 } 1744 else { 1745 $isBigEndian = false; 1746 $aux = "II"; 1747 $pos = $this->_putString($data, $pos, $aux); 1748 } 1749 $pos = $this->_putShort($data, $pos, 0x002A, $isBigEndian); 1750 $pos = $this->_putLong($data, $pos, 0x00000008, $isBigEndian); // IFD0 Offset is always 8 1751 1752 $ifd0 =& $this->_getIFDEntries($isBigEndian, 'ifd0'); 1753 $ifd1 =& $this->_getIFDEntries($isBigEndian, 'ifd1'); 1754 1755 $pos = $this->_writeIFD($data, $pos, $offsetBase, $ifd0, $isBigEndian, true); 1756 $pos = $this->_writeIFD($data, $pos, $offsetBase, $ifd1, $isBigEndian, false); 1757 1758 return $data; 1759 } 1760 1761 /*************************************************************/ 1762 function _writeIFD(&$data, $pos, $offsetBase, &$entries, $isBigEndian, $hasNext) 1763 { 1764 $tiffData = null; 1765 $tiffDataOffsetPos = -1; 1766 1767 $entryCount = count($entries); 1768 1769 $dataPos = $pos + 2 + ($entryCount * 12) + 4; 1770 $pos = $this->_putShort($data, $pos, $entryCount, $isBigEndian); 1771 1772 for ($i = 0; $i < $entryCount; $i++) { 1773 $tag = $entries[$i]['tag']; 1774 $type = $entries[$i]['type']; 1775 1776 if ($type == -99) { // SubIFD 1777 $pos = $this->_putShort($data, $pos, $tag, $isBigEndian); 1778 $pos = $this->_putShort($data, $pos, 0x04, $isBigEndian); // LONG 1779 $pos = $this->_putLong($data, $pos, 0x01, $isBigEndian); // Count = 1 1780 $pos = $this->_putLong($data, $pos, $dataPos - $offsetBase, $isBigEndian); 1781 1782 $dataPos = $this->_writeIFD($data, $dataPos, $offsetBase, $entries[$i]['value'], $isBigEndian, false); 1783 } 1784 elseif ($type == -98) { // TIFF Data 1785 $pos = $this->_putShort($data, $pos, $tag, $isBigEndian); 1786 $pos = $this->_putShort($data, $pos, 0x04, $isBigEndian); // LONG 1787 $pos = $this->_putLong($data, $pos, 0x01, $isBigEndian); // Count = 1 1788 $tiffDataOffsetPos = $pos; 1789 $pos = $this->_putLong($data, $pos, 0x00, $isBigEndian); // For Now 1790 $tiffData =& $entries[$i]['value'] ; 1791 } 1792 else { // Regular Entry 1793 $pos = $this->_putShort($data, $pos, $tag, $isBigEndian); 1794 $pos = $this->_putShort($data, $pos, $type, $isBigEndian); 1795 $pos = $this->_putLong($data, $pos, $entries[$i]['count'], $isBigEndian); 1796 if (strlen($entries[$i]['value']) > 4) { 1797 $pos = $this->_putLong($data, $pos, $dataPos - $offsetBase, $isBigEndian); 1798 $dataPos = $this->_putString($data, $dataPos, $entries[$i]['value']); 1799 } 1800 else { 1801 $val = str_pad($entries[$i]['value'], 4, "\0"); 1802 $pos = $this->_putString($data, $pos, $val); 1803 } 1804 } 1805 } 1806 1807 if ($tiffData != null) { 1808 $this->_putLong($data, $tiffDataOffsetPos, $dataPos - $offsetBase, $isBigEndian); 1809 $dataPos = $this->_putString($data, $dataPos, $tiffData); 1810 } 1811 1812 if ($hasNext) { 1813 $pos = $this->_putLong($data, $pos, $dataPos - $offsetBase, $isBigEndian); 1814 } 1815 else { 1816 $pos = $this->_putLong($data, $pos, 0, $isBigEndian); 1817 } 1818 1819 return $dataPos; 1820 } 1821 1822 /*************************************************************/ 1823 function & _getIFDEntries($isBigEndian, $mode) 1824 { 1825 $EXIFNames = $this->_exifTagNames($mode); 1826 $EXIFTags = $this->_exifNameTags($mode); 1827 $EXIFTypeInfo = $this->_exifTagTypes($mode); 1828 1829 $ifdEntries = array(); 1830 $entryCount = 0; 1831 1832 reset($EXIFNames); 1833 while (list($tag, $name) = each($EXIFNames)) { 1834 $type = $EXIFTypeInfo[$tag][0]; 1835 $count = $EXIFTypeInfo[$tag][1]; 1836 $value = null; 1837 1838 if (($mode == 'ifd0') && ($tag == 0x8769)) { // ExifIFDOffset 1839 if (isset($this->_info['exif']['EXIFVersion'])) { 1840 $value =& $this->_getIFDEntries($isBigEndian, "exif"); 1841 $type = -99; 1842 } 1843 else { 1844 $value = null; 1845 } 1846 } 1847 elseif (($mode == 'ifd0') && ($tag == 0x8825)) { // GPSIFDOffset 1848 if (isset($this->_info['exif']['GPSVersionID'])) { 1849 $value =& $this->_getIFDEntries($isBigEndian, "gps"); 1850 $type = -99; 1851 } 1852 else { 1853 $value = null; 1854 } 1855 } 1856 elseif (($mode == 'ifd1') && ($tag == 0x0111)) { // TIFFStripOffsets 1857 if (isset($this->_info['exif']['TIFFStrips'])) { 1858 $value =& $this->_info['exif']['TIFFStrips']; 1859 $type = -98; 1860 } 1861 else { 1862 $value = null; 1863 } 1864 } 1865 elseif (($mode == 'ifd1') && ($tag == 0x0117)) { // TIFFStripByteCounts 1866 if (isset($this->_info['exif']['TIFFStrips'])) { 1867 $value = strlen($this->_info['exif']['TIFFStrips']); 1868 } 1869 else { 1870 $value = null; 1871 } 1872 } 1873 elseif (($mode == 'ifd1') && ($tag == 0x0201)) { // TIFFJFIFOffset 1874 if (isset($this->_info['exif']['JFIFThumbnail'])) { 1875 $value =& $this->_info['exif']['JFIFThumbnail']; 1876 $type = -98; 1877 } 1878 else { 1879 $value = null; 1880 } 1881 } 1882 elseif (($mode == 'ifd1') && ($tag == 0x0202)) { // TIFFJFIFLength 1883 if (isset($this->_info['exif']['JFIFThumbnail'])) { 1884 $value = strlen($this->_info['exif']['JFIFThumbnail']); 1885 } 1886 else { 1887 $value = null; 1888 } 1889 } 1890 elseif (($mode == 'exif') && ($tag == 0xA005)) { // InteropIFDOffset 1891 if (isset($this->_info['exif']['InteroperabilityIndex'])) { 1892 $value =& $this->_getIFDEntries($isBigEndian, "interop"); 1893 $type = -99; 1894 } 1895 else { 1896 $value = null; 1897 } 1898 } 1899 elseif (isset($this->_info['exif'][$name])) { 1900 $origValue =& $this->_info['exif'][$name]; 1901 1902 // This makes it easier to process variable size elements 1903 if (!is_array($origValue) || isset($origValue['val'])) { 1904 unset($origValue); // Break the reference 1905 $origValue = array($this->_info['exif'][$name]); 1906 } 1907 $origCount = count($origValue); 1908 1909 if ($origCount == 0 ) { 1910 $type = -1; // To ignore this field 1911 } 1912 1913 $value = " "; 1914 1915 switch ($type) { 1916 case 1: // UBYTE 1917 if ($count == 0) { 1918 $count = $origCount; 1919 } 1920 1921 $j = 0; 1922 while (($j < $count) && ($j < $origCount)) { 1923 1924 $this->_putByte($value, $j, $origValue[$j]); 1925 $j++; 1926 } 1927 1928 while ($j < $count) { 1929 $this->_putByte($value, $j, 0); 1930 $j++; 1931 } 1932 break; 1933 case 2: // ASCII 1934 $v = strval($origValue[0]); 1935 if (($count != 0) && (strlen($v) > $count)) { 1936 $v = substr($v, 0, $count); 1937 } 1938 elseif (($count > 0) && (strlen($v) < $count)) { 1939 $v = str_pad($v, $count, "\0"); 1940 } 1941 1942 $count = strlen($v); 1943 1944 $this->_putString($value, 0, $v); 1945 break; 1946 case 3: // USHORT 1947 if ($count == 0) { 1948 $count = $origCount; 1949 } 1950 1951 $j = 0; 1952 while (($j < $count) && ($j < $origCount)) { 1953 $this->_putShort($value, $j * 2, $origValue[$j], $isBigEndian); 1954 $j++; 1955 } 1956 1957 while ($j < $count) { 1958 $this->_putShort($value, $j * 2, 0, $isBigEndian); 1959 $j++; 1960 } 1961 break; 1962 case 4: // ULONG 1963 if ($count == 0) { 1964 $count = $origCount; 1965 } 1966 1967 $j = 0; 1968 while (($j < $count) && ($j < $origCount)) { 1969 $this->_putLong($value, $j * 4, $origValue[$j], $isBigEndian); 1970 $j++; 1971 } 1972 1973 while ($j < $count) { 1974 $this->_putLong($value, $j * 4, 0, $isBigEndian); 1975 $j++; 1976 } 1977 break; 1978 case 5: // URATIONAL 1979 if ($count == 0) { 1980 $count = $origCount; 1981 } 1982 1983 $j = 0; 1984 while (($j < $count) && ($j < $origCount)) { 1985 $v = $origValue[$j]; 1986 if (is_array($v)) { 1987 $a = $v['num']; 1988 $b = $v['den']; 1989 } 1990 else { 1991 $a = 0; 1992 $b = 0; 1993 // TODO: Allow other types and convert them 1994 } 1995 $this->_putLong($value, $j * 8, $a, $isBigEndian); 1996 $this->_putLong($value, ($j * 8) + 4, $b, $isBigEndian); 1997 $j++; 1998 } 1999 2000 while ($j < $count) { 2001 $this->_putLong($value, $j * 8, 0, $isBigEndian); 2002 $this->_putLong($value, ($j * 8) + 4, 0, $isBigEndian); 2003 $j++; 2004 } 2005 break; 2006 case 6: // SBYTE 2007 if ($count == 0) { 2008 $count = $origCount; 2009 } 2010 2011 $j = 0; 2012 while (($j < $count) && ($j < $origCount)) { 2013 $this->_putByte($value, $j, $origValue[$j]); 2014 $j++; 2015 } 2016 2017 while ($j < $count) { 2018 $this->_putByte($value, $j, 0); 2019 $j++; 2020 } 2021 break; 2022 case 7: // UNDEFINED 2023 $v = strval($origValue[0]); 2024 if (($count != 0) && (strlen($v) > $count)) { 2025 $v = substr($v, 0, $count); 2026 } 2027 elseif (($count > 0) && (strlen($v) < $count)) { 2028 $v = str_pad($v, $count, "\0"); 2029 } 2030 2031 $count = strlen($v); 2032 2033 $this->_putString($value, 0, $v); 2034 break; 2035 case 8: // SSHORT 2036 if ($count == 0) { 2037 $count = $origCount; 2038 } 2039 2040 $j = 0; 2041 while (($j < $count) && ($j < $origCount)) { 2042 $this->_putShort($value, $j * 2, $origValue[$j], $isBigEndian); 2043 $j++; 2044 } 2045 2046 while ($j < $count) { 2047 $this->_putShort($value, $j * 2, 0, $isBigEndian); 2048 $j++; 2049 } 2050 break; 2051 case 9: // SLONG 2052 if ($count == 0) { 2053 $count = $origCount; 2054 } 2055 2056 $j = 0; 2057 while (($j < $count) && ($j < $origCount)) { 2058 $this->_putLong($value, $j * 4, $origValue[$j], $isBigEndian); 2059 $j++; 2060 } 2061 2062 while ($j < $count) { 2063 $this->_putLong($value, $j * 4, 0, $isBigEndian); 2064 $j++; 2065 } 2066 break; 2067 case 10: // SRATIONAL 2068 if ($count == 0) { 2069 $count = $origCount; 2070 } 2071 2072 $j = 0; 2073 while (($j < $count) && ($j < $origCount)) { 2074 $v = $origValue[$j]; 2075 if (is_array($v)) { 2076 $a = $v['num']; 2077 $b = $v['den']; 2078 } 2079 else { 2080 $a = 0; 2081 $b = 0; 2082 // TODO: Allow other types and convert them 2083 } 2084 2085 $this->_putLong($value, $j * 8, $a, $isBigEndian); 2086 $this->_putLong($value, ($j * 8) + 4, $b, $isBigEndian); 2087 $j++; 2088 } 2089 2090 while ($j < $count) { 2091 $this->_putLong($value, $j * 8, 0, $isBigEndian); 2092 $this->_putLong($value, ($j * 8) + 4, 0, $isBigEndian); 2093 $j++; 2094 } 2095 break; 2096 case 11: // FLOAT 2097 if ($count == 0) { 2098 $count = $origCount; 2099 } 2100 2101 $j = 0; 2102 while (($j < $count) && ($j < $origCount)) { 2103 $v = strval($origValue[$j]); 2104 if (strlen($v) > 4) { 2105 $v = substr($v, 0, 4); 2106 } 2107 elseif (strlen($v) < 4) { 2108 $v = str_pad($v, 4, "\0"); 2109 } 2110 $this->_putString($value, $j * 4, $v); 2111 $j++; 2112 } 2113 2114 while ($j < $count) { 2115 $this->_putString($value, $j * 4, "\0\0\0\0"); 2116 $j++; 2117 } 2118 break; 2119 case 12: // DFLOAT 2120 if ($count == 0) { 2121 $count = $origCount; 2122 } 2123 2124 $j = 0; 2125 while (($j < $count) && ($j < $origCount)) { 2126 $v = strval($origValue[$j]); 2127 if (strlen($v) > 8) { 2128 $v = substr($v, 0, 8); 2129 } 2130 elseif (strlen($v) < 8) { 2131 $v = str_pad($v, 8, "\0"); 2132 } 2133 $this->_putString($value, $j * 8, $v); 2134 $j++; 2135 } 2136 2137 while ($j < $count) { 2138 $this->_putString($value, $j * 8, "\0\0\0\0\0\0\0\0"); 2139 $j++; 2140 } 2141 break; 2142 default: 2143 $value = null; 2144 break; 2145 } 2146 } 2147 2148 if ($value != null) { 2149 $ifdEntries[$entryCount] = array(); 2150 $ifdEntries[$entryCount]['tag'] = $tag; 2151 $ifdEntries[$entryCount]['type'] = $type; 2152 $ifdEntries[$entryCount]['count'] = $count; 2153 $ifdEntries[$entryCount]['value'] = $value; 2154 2155 $entryCount++; 2156 } 2157 } 2158 2159 return $ifdEntries; 2160 } 2161 2162 /*************************************************************/ 2163 function _parseMarkerAdobe() 2164 { 2165 if (!isset($this->_markers)) { 2166 $this->_readJPEG(); 2167 } 2168 2169 if ($this->_markers == null) { 2170 return false; 2171 } 2172 2173 $data = null; 2174 $count = count($this->_markers); 2175 for ($i = 0; $i < $count; $i++) { 2176 if ($this->_markers[$i]['marker'] == 0xED) { 2177 $signature = $this->_getFixedString($this->_markers[$i]['data'], 0, 14); 2178 if ($signature == "Photoshop 3.0\0") { 2179 $data =& $this->_markers[$i]['data']; 2180 break; 2181 } 2182 } 2183 } 2184 2185 if ($data == null) { 2186 $this->_info['adobe'] = false; 2187 $this->_info['iptc'] = false; 2188 return false; 2189 } 2190 $pos = 14; 2191 $this->_info['adobe'] = array(); 2192 $this->_info['adobe']['raw'] = array(); 2193 $this->_info['iptc'] = array(); 2194 2195 $datasize = strlen($data); 2196 2197 while ($pos < $datasize) { 2198 $signature = $this->_getFixedString($data, $pos, 4); 2199 if ($signature != '8BIM') 2200 return false; 2201 $pos += 4; 2202 2203 $type = $this->_getShort($data, $pos); 2204 $pos += 2; 2205 2206 $strlen = $this->_getByte($data, $pos); 2207 $pos += 1; 2208 $header = ''; 2209 for ($i = 0; $i < $strlen; $i++) { 2210 $header .= $data{$pos + $i}; 2211 } 2212 $pos += $strlen + 1 - ($strlen % 2); // The string is padded to even length, counting the length byte itself 2213 2214 $length = $this->_getLong($data, $pos); 2215 $pos += 4; 2216 2217 $basePos = $pos; 2218 2219 switch ($type) { 2220 case 0x0404: // Caption (IPTC Data) 2221 $pos = $this->_readIPTC($data, $pos); 2222 if ($pos == false) 2223 return false; 2224 break; 2225 case 0x040A: // CopyrightFlag 2226 $this->_info['adobe']['CopyrightFlag'] = $this->_getByte($data, $pos); 2227 $pos += $length; 2228 break; 2229 case 0x040B: // ImageURL 2230 $this->_info['adobe']['ImageURL'] = $this->_getFixedString($data, $pos, $length); 2231 $pos += $length; 2232 break; 2233 case 0x040C: // Thumbnail 2234 $aux = $this->_getLong($data, $pos); 2235 $pos += 4; 2236 if ($aux == 1) { 2237 $this->_info['adobe']['ThumbnailWidth'] = $this->_getLong($data, $pos); 2238 $pos += 4; 2239 $this->_info['adobe']['ThumbnailHeight'] = $this->_getLong($data, $pos); 2240 $pos += 4; 2241 2242 $pos += 16; // Skip some data 2243 2244 $this->_info['adobe']['ThumbnailData'] = $this->_getFixedString($data, $pos, $length - 28); 2245 $pos += $length - 28; 2246 } 2247 break; 2248 default: 2249 break; 2250 } 2251 2252 // We save all blocks, even those we recognized 2253 $label = sprintf('8BIM_0x%04x', $type); 2254 $this->_info['adobe']['raw'][$label] = array(); 2255 $this->_info['adobe']['raw'][$label]['type'] = $type; 2256 $this->_info['adobe']['raw'][$label]['header'] = $header; 2257 $this->_info['adobe']['raw'][$label]['data'] =& $this->_getFixedString($data, $basePos, $length); 2258 2259 $pos = $basePos + $length + ($length % 2); // Even padding 2260 } 2261 2262 } 2263 2264 /*************************************************************/ 2265 function _readIPTC(&$data, $pos = 0) 2266 { 2267 $totalLength = strlen($data); 2268 2269 $IPTCTags =& $this->_iptcTagNames(); 2270 2271 while ($pos < ($totalLength - 5)) { 2272 $signature = $this->_getShort($data, $pos); 2273 if ($signature != 0x1C02) 2274 return $pos; 2275 $pos += 2; 2276 2277 $type = $this->_getByte($data, $pos); 2278 $pos += 1; 2279 $length = $this->_getShort($data, $pos); 2280 $pos += 2; 2281 2282 $basePos = $pos; 2283 $label = ''; 2284 2285 if (isset($IPTCTags[$type])) { 2286 $label = $IPTCTags[$type]; 2287 } 2288 else { 2289 $label = sprintf('IPTC_0x%02x', $type); 2290 } 2291 2292 if ($label != '') { 2293 if (isset($this->_info['iptc'][$label])) { 2294 if (!is_array($this->_info['iptc'][$label])) { 2295 $aux = array(); 2296 $aux[0] = $this->_info['iptc'][$label]; 2297 $this->_info['iptc'][$label] = $aux; 2298 } 2299 $this->_info['iptc'][$label][ count($this->_info['iptc'][$label]) ] = $this->_getFixedString($data, $pos, $length); 2300 } 2301 else { 2302 $this->_info['iptc'][$label] = $this->_getFixedString($data, $pos, $length); 2303 } 2304 } 2305 2306 $pos = $basePos + $length; // No padding 2307 } 2308 return $pos; 2309 } 2310 2311 /*************************************************************/ 2312 function & _createMarkerAdobe() 2313 { 2314 if (isset($this->_info['iptc'])) { 2315 if (!isset($this->_info['adobe'])) { 2316 $this->_info['adobe'] = array(); 2317 } 2318 if (!isset($this->_info['adobe']['raw'])) { 2319 $this->_info['adobe']['raw'] = array(); 2320 } 2321 if (!isset($this->_info['adobe']['raw']['8BIM_0x0404'])) { 2322 $this->_info['adobe']['raw']['8BIM_0x0404'] = array(); 2323 } 2324 $this->_info['adobe']['raw']['8BIM_0x0404']['type'] = 0x0404; 2325 $this->_info['adobe']['raw']['8BIM_0x0404']['header'] = "Caption"; 2326 $this->_info['adobe']['raw']['8BIM_0x0404']['data'] =& $this->_writeIPTC(); 2327 } 2328 2329 if (isset($this->_info['adobe']['raw']) && (count($this->_info['adobe']['raw']) > 0)) { 2330 $data = "Photoshop 3.0\0"; 2331 $pos = 14; 2332 2333 reset($this->_info['adobe']['raw']); 2334 while (list($key) = each($this->_info['adobe']['raw'])) { 2335 $pos = $this->_write8BIM( 2336 $data, 2337 $pos, 2338 $this->_info['adobe']['raw'][$key]['type'], 2339 $this->_info['adobe']['raw'][$key]['header'], 2340 $this->_info['adobe']['raw'][$key]['data'] ); 2341 } 2342 } 2343 2344 return $data; 2345 } 2346 2347 /*************************************************************/ 2348 function _write8BIM(&$data, $pos, $type, $header, &$value) 2349 { 2350 $signature = "8BIM"; 2351 2352 $pos = $this->_putString($data, $pos, $signature); 2353 $pos = $this->_putShort($data, $pos, $type); 2354 2355 $len = strlen($header); 2356 2357 $pos = $this->_putByte($data, $pos, $len); 2358 $pos = $this->_putString($data, $pos, $header); 2359 if (($len % 2) == 0) { // Even padding, including the length byte 2360 $pos = $this->_putByte($data, $pos, 0); 2361 } 2362 2363 $len = strlen($value); 2364 $pos = $this->_putLong($data, $pos, $len); 2365 $pos = $this->_putString($data, $pos, $value); 2366 if (($len % 2) != 0) { // Even padding 2367 $pos = $this->_putByte($data, $pos, 0); 2368 } 2369 return $pos; 2370 } 2371 2372 /*************************************************************/ 2373 function & _writeIPTC() 2374 { 2375 $data = " "; 2376 $pos = 0; 2377 2378 $IPTCNames =& $this->_iptcNameTags(); 2379 2380 reset($this->_info['iptc']); 2381 2382 2383 while (list($label) = each($this->_info['iptc'])) { 2384 $value =& $this->_info['iptc'][$label]; 2385 $type = -1; 2386 2387 if (isset($IPTCNames[$label])) { 2388 $type = $IPTCNames[$label]; 2389 } 2390 elseif (substr($label, 0, 7) == "IPTC_0x") { 2391 $type = hexdec(substr($label, 7, 2)); 2392 } 2393 2394 if ($type != -1) { 2395 if (is_array($value)) { 2396 for ($i = 0; $i < count($value); $i++) { 2397 $pos = $this->_writeIPTCEntry($data, $pos, $type, $value[$i]); 2398 } 2399 } 2400 else { 2401 $pos = $this->_writeIPTCEntry($data, $pos, $type, $value); 2402 } 2403 } 2404 } 2405 2406 return $data; 2407 } 2408 2409 /*************************************************************/ 2410 function _writeIPTCEntry(&$data, $pos, $type, &$value) 2411 { 2412 $pos = $this->_putShort($data, $pos, 0x1C02); 2413 $pos = $this->_putByte($data, $pos, $type); 2414 $pos = $this->_putShort($data, $pos, strlen($value)); 2415 $pos = $this->_putString($data, $pos, $value); 2416 2417 return $pos; 2418 } 2419 2420 /*************************************************************/ 2421 function _exifTagNames($mode) 2422 { 2423 $tags = array(); 2424 2425 if ($mode == 'ifd0') { 2426 $tags[0x010E] = 'ImageDescription'; 2427 $tags[0x010F] = 'Make'; 2428 $tags[0x0110] = 'Model'; 2429 $tags[0x0112] = 'Orientation'; 2430 $tags[0x011A] = 'XResolution'; 2431 $tags[0x011B] = 'YResolution'; 2432 $tags[0x0128] = 'ResolutionUnit'; 2433 $tags[0x0131] = 'Software'; 2434 $tags[0x0132] = 'DateTime'; 2435 $tags[0x013B] = 'Artist'; 2436 $tags[0x013E] = 'WhitePoint'; 2437 $tags[0x013F] = 'PrimaryChromaticities'; 2438 $tags[0x0211] = 'YCbCrCoefficients'; 2439 $tags[0x0212] = 'YCbCrSubSampling'; 2440 $tags[0x0213] = 'YCbCrPositioning'; 2441 $tags[0x0214] = 'ReferenceBlackWhite'; 2442 $tags[0x8298] = 'Copyright'; 2443 $tags[0x8769] = 'ExifIFDOffset'; 2444 $tags[0x8825] = 'GPSIFDOffset'; 2445 } 2446 if ($mode == 'ifd1') { 2447 $tags[0x00FE] = 'TIFFNewSubfileType'; 2448 $tags[0x00FF] = 'TIFFSubfileType'; 2449 $tags[0x0100] = 'TIFFImageWidth'; 2450 $tags[0x0101] = 'TIFFImageHeight'; 2451 $tags[0x0102] = 'TIFFBitsPerSample'; 2452 $tags[0x0103] = 'TIFFCompression'; 2453 $tags[0x0106] = 'TIFFPhotometricInterpretation'; 2454 $tags[0x0107] = 'TIFFThreshholding'; 2455 $tags[0x0108] = 'TIFFCellWidth'; 2456 $tags[0x0109] = 'TIFFCellLength'; 2457 $tags[0x010A] = 'TIFFFillOrder'; 2458 $tags[0x010E] = 'TIFFImageDescription'; 2459 $tags[0x010F] = 'TIFFMake'; 2460 $tags[0x0110] = 'TIFFModel'; 2461 $tags[0x0111] = 'TIFFStripOffsets'; 2462 $tags[0x0112] = 'TIFFOrientation'; 2463 $tags[0x0115] = 'TIFFSamplesPerPixel'; 2464 $tags[0x0116] = 'TIFFRowsPerStrip'; 2465 $tags[0x0117] = 'TIFFStripByteCounts'; 2466 $tags[0x0118] = 'TIFFMinSampleValue'; 2467 $tags[0x0119] = 'TIFFMaxSampleValue'; 2468 $tags[0x011A] = 'TIFFXResolution'; 2469 $tags[0x011B] = 'TIFFYResolution'; 2470 $tags[0x011C] = 'TIFFPlanarConfiguration'; 2471 $tags[0x0122] = 'TIFFGrayResponseUnit'; 2472 $tags[0x0123] = 'TIFFGrayResponseCurve'; 2473 $tags[0x0128] = 'TIFFResolutionUnit'; 2474 $tags[0x0131] = 'TIFFSoftware'; 2475 $tags[0x0132] = 'TIFFDateTime'; 2476 $tags[0x013B] = 'TIFFArtist'; 2477 $tags[0x013C] = 'TIFFHostComputer'; 2478 $tags[0x0140] = 'TIFFColorMap'; 2479 $tags[0x0152] = 'TIFFExtraSamples'; 2480 $tags[0x0201] = 'TIFFJFIFOffset'; 2481 $tags[0x0202] = 'TIFFJFIFLength'; 2482 $tags[0x0211] = 'TIFFYCbCrCoefficients'; 2483 $tags[0x0212] = 'TIFFYCbCrSubSampling'; 2484 $tags[0x0213] = 'TIFFYCbCrPositioning'; 2485 $tags[0x0214] = 'TIFFReferenceBlackWhite'; 2486 $tags[0x8298] = 'TIFFCopyright'; 2487 $tags[0x9286] = 'TIFFUserComment'; 2488 } 2489 elseif ($mode == 'exif') { 2490 $tags[0x829A] = 'ExposureTime'; 2491 $tags[0x829D] = 'FNumber'; 2492 $tags[0x8822] = 'ExposureProgram'; 2493 $tags[0x8824] = 'SpectralSensitivity'; 2494 $tags[0x8827] = 'ISOSpeedRatings'; 2495 $tags[0x8828] = 'OECF'; 2496 $tags[0x9000] = 'EXIFVersion'; 2497 $tags[0x9003] = 'DatetimeOriginal'; 2498 $tags[0x9004] = 'DatetimeDigitized'; 2499 $tags[0x9101] = 'ComponentsConfiguration'; 2500 $tags[0x9102] = 'CompressedBitsPerPixel'; 2501 $tags[0x9201] = 'ShutterSpeedValue'; 2502 $tags[0x9202] = 'ApertureValue'; 2503 $tags[0x9203] = 'BrightnessValue'; 2504 $tags[0x9204] = 'ExposureBiasValue'; 2505 $tags[0x9205] = 'MaxApertureValue'; 2506 $tags[0x9206] = 'SubjectDistance'; 2507 $tags[0x9207] = 'MeteringMode'; 2508 $tags[0x9208] = 'LightSource'; 2509 $tags[0x9209] = 'Flash'; 2510 $tags[0x920A] = 'FocalLength'; 2511 $tags[0x927C] = 'MakerNote'; 2512 $tags[0x9286] = 'UserComment'; 2513 $tags[0x9290] = 'SubSecTime'; 2514 $tags[0x9291] = 'SubSecTimeOriginal'; 2515 $tags[0x9292] = 'SubSecTimeDigitized'; 2516 $tags[0xA000] = 'FlashPixVersion'; 2517 $tags[0xA001] = 'ColorSpace'; 2518 $tags[0xA002] = 'PixelXDimension'; 2519 $tags[0xA003] = 'PixelYDimension'; 2520 $tags[0xA004] = 'RelatedSoundFile'; 2521 $tags[0xA005] = 'InteropIFDOffset'; 2522 $tags[0xA20B] = 'FlashEnergy'; 2523 $tags[0xA20C] = 'SpatialFrequencyResponse'; 2524 $tags[0xA20E] = 'FocalPlaneXResolution'; 2525 $tags[0xA20F] = 'FocalPlaneYResolution'; 2526 $tags[0xA210] = 'FocalPlaneResolutionUnit'; 2527 $tags[0xA214] = 'SubjectLocation'; 2528 $tags[0xA215] = 'ExposureIndex'; 2529 $tags[0xA217] = 'SensingMethod'; 2530 $tags[0xA300] = 'FileSource'; 2531 $tags[0xA301] = 'SceneType'; 2532 $tags[0xA302] = 'CFAPattern'; 2533 } 2534 elseif ($mode == 'interop') { 2535 $tags[0x0001] = 'InteroperabilityIndex'; 2536 $tags[0x0002] = 'InteroperabilityVersion'; 2537 $tags[0x1000] = 'RelatedImageFileFormat'; 2538 $tags[0x1001] = 'RelatedImageWidth'; 2539 $tags[0x1002] = 'RelatedImageLength'; 2540 } 2541 elseif ($mode == 'gps') { 2542 $tags[0x0000] = 'GPSVersionID'; 2543 $tags[0x0001] = 'GPSLatitudeRef'; 2544 $tags[0x0002] = 'GPSLatitude'; 2545 $tags[0x0003] = 'GPSLongitudeRef'; 2546 $tags[0x0004] = 'GPSLongitude'; 2547 $tags[0x0005] = 'GPSAltitudeRef'; 2548 $tags[0x0006] = 'GPSAltitude'; 2549 $tags[0x0007] = 'GPSTimeStamp'; 2550 $tags[0x0008] = 'GPSSatellites'; 2551 $tags[0x0009] = 'GPSStatus'; 2552 $tags[0x000A] = 'GPSMeasureMode'; 2553 $tags[0x000B] = 'GPSDOP'; 2554 $tags[0x000C] = 'GPSSpeedRef'; 2555 $tags[0x000D] = 'GPSSpeed'; 2556 $tags[0x000E] = 'GPSTrackRef'; 2557 $tags[0x000F] = 'GPSTrack'; 2558 $tags[0x0010] = 'GPSImgDirectionRef'; 2559 $tags[0x0011] = 'GPSImgDirection'; 2560 $tags[0x0012] = 'GPSMapDatum'; 2561 $tags[0x0013] = 'GPSDestLatitudeRef'; 2562 $tags[0x0014] = 'GPSDestLatitude'; 2563 $tags[0x0015] = 'GPSDestLongitudeRef'; 2564 $tags[0x0016] = 'GPSDestLongitude'; 2565 $tags[0x0017] = 'GPSDestBearingRef'; 2566 $tags[0x0018] = 'GPSDestBearing'; 2567 $tags[0x0019] = 'GPSDestDistanceRef'; 2568 $tags[0x001A] = 'GPSDestDistance'; 2569 } 2570 2571 return $tags; 2572 } 2573 2574 /*************************************************************/ 2575 function _exifTagTypes($mode) 2576 { 2577 $tags = array(); 2578 2579 if ($mode == 'ifd0') { 2580 $tags[0x010E] = array(2, 0); // ImageDescription -> ASCII, Any 2581 $tags[0x010F] = array(2, 0); // Make -> ASCII, Any 2582 $tags[0x0110] = array(2, 0); // Model -> ASCII, Any 2583 $tags[0x0112] = array(3, 1); // Orientation -> SHORT, 1 2584 $tags[0x011A] = array(5, 1); // XResolution -> RATIONAL, 1 2585 $tags[0x011B] = array(5, 1); // YResolution -> RATIONAL, 1 2586 $tags[0x0128] = array(3, 1); // ResolutionUnit -> SHORT 2587 $tags[0x0131] = array(2, 0); // Software -> ASCII, Any 2588 $tags[0x0132] = array(2, 20); // DateTime -> ASCII, 20 2589 $tags[0x013B] = array(2, 0); // Artist -> ASCII, Any 2590 $tags[0x013E] = array(5, 2); // WhitePoint -> RATIONAL, 2 2591 $tags[0x013F] = array(5, 6); // PrimaryChromaticities -> RATIONAL, 6 2592 $tags[0x0211] = array(5, 3); // YCbCrCoefficients -> RATIONAL, 3 2593 $tags[0x0212] = array(3, 2); // YCbCrSubSampling -> SHORT, 2 2594 $tags[0x0213] = array(3, 1); // YCbCrPositioning -> SHORT, 1 2595 $tags[0x0214] = array(5, 6); // ReferenceBlackWhite -> RATIONAL, 6 2596 $tags[0x8298] = array(2, 0); // Copyright -> ASCII, Any 2597 $tags[0x8769] = array(4, 1); // ExifIFDOffset -> LONG, 1 2598 $tags[0x8825] = array(4, 1); // GPSIFDOffset -> LONG, 1 2599 } 2600 if ($mode == 'ifd1') { 2601 $tags[0x00FE] = array(4, 1); // TIFFNewSubfileType -> LONG, 1 2602 $tags[0x00FF] = array(3, 1); // TIFFSubfileType -> SHORT, 1 2603 $tags[0x0100] = array(4, 1); // TIFFImageWidth -> LONG (or SHORT), 1 2604 $tags[0x0101] = array(4, 1); // TIFFImageHeight -> LONG (or SHORT), 1 2605 $tags[0x0102] = array(3, 3); // TIFFBitsPerSample -> SHORT, 3 2606 $tags[0x0103] = array(3, 1); // TIFFCompression -> SHORT, 1 2607 $tags[0x0106] = array(3, 1); // TIFFPhotometricInterpretation -> SHORT, 1 2608 $tags[0x0107] = array(3, 1); // TIFFThreshholding -> SHORT, 1 2609 $tags[0x0108] = array(3, 1); // TIFFCellWidth -> SHORT, 1 2610 $tags[0x0109] = array(3, 1); // TIFFCellLength -> SHORT, 1 2611 $tags[0x010A] = array(3, 1); // TIFFFillOrder -> SHORT, 1 2612 $tags[0x010E] = array(2, 0); // TIFFImageDescription -> ASCII, Any 2613 $tags[0x010F] = array(2, 0); // TIFFMake -> ASCII, Any 2614 $tags[0x0110] = array(2, 0); // TIFFModel -> ASCII, Any 2615 $tags[0x0111] = array(4, 0); // TIFFStripOffsets -> LONG (or SHORT), Any (one per strip) 2616 $tags[0x0112] = array(3, 1); // TIFFOrientation -> SHORT, 1 2617 $tags[0x0115] = array(3, 1); // TIFFSamplesPerPixel -> SHORT, 1 2618 $tags[0x0116] = array(4, 1); // TIFFRowsPerStrip -> LONG (or SHORT), 1 2619 $tags[0x0117] = array(4, 0); // TIFFStripByteCounts -> LONG (or SHORT), Any (one per strip) 2620 $tags[0x0118] = array(3, 0); // TIFFMinSampleValue -> SHORT, Any (SamplesPerPixel) 2621 $tags[0x0119] = array(3, 0); // TIFFMaxSampleValue -> SHORT, Any (SamplesPerPixel) 2622 $tags[0x011A] = array(5, 1); // TIFFXResolution -> RATIONAL, 1 2623 $tags[0x011B] = array(5, 1); // TIFFYResolution -> RATIONAL, 1 2624 $tags[0x011C] = array(3, 1); // TIFFPlanarConfiguration -> SHORT, 1 2625 $tags[0x0122] = array(3, 1); // TIFFGrayResponseUnit -> SHORT, 1 2626 $tags[0x0123] = array(3, 0); // TIFFGrayResponseCurve -> SHORT, Any (2^BitsPerSample) 2627 $tags[0x0128] = array(3, 1); // TIFFResolutionUnit -> SHORT, 1 2628 $tags[0x0131] = array(2, 0); // TIFFSoftware -> ASCII, Any 2629 $tags[0x0132] = array(2, 20); // TIFFDateTime -> ASCII, 20 2630 $tags[0x013B] = array(2, 0); // TIFFArtist -> ASCII, Any 2631 $tags[0x013C] = array(2, 0); // TIFFHostComputer -> ASCII, Any 2632 $tags[0x0140] = array(3, 0); // TIFFColorMap -> SHORT, Any (3 * 2^BitsPerSample) 2633 $tags[0x0152] = array(3, 0); // TIFFExtraSamples -> SHORT, Any (SamplesPerPixel - 3) 2634 $tags[0x0201] = array(4, 1); // TIFFJFIFOffset -> LONG, 1 2635 $tags[0x0202] = array(4, 1); // TIFFJFIFLength -> LONG, 1 2636 $tags[0x0211] = array(5, 3); // TIFFYCbCrCoefficients -> RATIONAL, 3 2637 $tags[0x0212] = array(3, 2); // TIFFYCbCrSubSampling -> SHORT, 2 2638 $tags[0x0213] = array(3, 1); // TIFFYCbCrPositioning -> SHORT, 1 2639 $tags[0x0214] = array(5, 6); // TIFFReferenceBlackWhite -> RATIONAL, 6 2640 $tags[0x8298] = array(2, 0); // TIFFCopyright -> ASCII, Any 2641 $tags[0x9286] = array(2, 0); // TIFFUserComment -> ASCII, Any 2642 } 2643 elseif ($mode == 'exif') { 2644 $tags[0x829A] = array(5, 1); // ExposureTime -> RATIONAL, 1 2645 $tags[0x829D] = array(5, 1); // FNumber -> RATIONAL, 1 2646 $tags[0x8822] = array(3, 1); // ExposureProgram -> SHORT, 1 2647 $tags[0x8824] = array(2, 0); // SpectralSensitivity -> ASCII, Any 2648 $tags[0x8827] = array(3, 0); // ISOSpeedRatings -> SHORT, Any 2649 $tags[0x8828] = array(7, 0); // OECF -> UNDEFINED, Any 2650 $tags[0x9000] = array(7, 4); // EXIFVersion -> UNDEFINED, 4 2651 $tags[0x9003] = array(2, 20); // DatetimeOriginal -> ASCII, 20 2652 $tags[0x9004] = array(2, 20); // DatetimeDigitized -> ASCII, 20 2653 $tags[0x9101] = array(7, 4); // ComponentsConfiguration -> UNDEFINED, 4 2654 $tags[0x9102] = array(5, 1); // CompressedBitsPerPixel -> RATIONAL, 1 2655 $tags[0x9201] = array(10, 1); // ShutterSpeedValue -> SRATIONAL, 1 2656 $tags[0x9202] = array(5, 1); // ApertureValue -> RATIONAL, 1 2657 $tags[0x9203] = array(10, 1); // BrightnessValue -> SRATIONAL, 1 2658 $tags[0x9204] = array(10, 1); // ExposureBiasValue -> SRATIONAL, 1 2659 $tags[0x9205] = array(5, 1); // MaxApertureValue -> RATIONAL, 1 2660 $tags[0x9206] = array(5, 1); // SubjectDistance -> RATIONAL, 1 2661 $tags[0x9207] = array(3, 1); // MeteringMode -> SHORT, 1 2662 $tags[0x9208] = array(3, 1); // LightSource -> SHORT, 1 2663 $tags[0x9209] = array(3, 1); // Flash -> SHORT, 1 2664 $tags[0x920A] = array(5, 1); // FocalLength -> RATIONAL, 1 2665 $tags[0x927C] = array(7, 0); // MakerNote -> UNDEFINED, Any 2666 $tags[0x9286] = array(7, 0); // UserComment -> UNDEFINED, Any 2667 $tags[0x9290] = array(2, 0); // SubSecTime -> ASCII, Any 2668 $tags[0x9291] = array(2, 0); // SubSecTimeOriginal -> ASCII, Any 2669 $tags[0x9292] = array(2, 0); // SubSecTimeDigitized -> ASCII, Any 2670 $tags[0xA000] = array(7, 4); // FlashPixVersion -> UNDEFINED, 4 2671 $tags[0xA001] = array(3, 1); // ColorSpace -> SHORT, 1 2672 $tags[0xA002] = array(4, 1); // PixelXDimension -> LONG (or SHORT), 1 2673 $tags[0xA003] = array(4, 1); // PixelYDimension -> LONG (or SHORT), 1 2674 $tags[0xA004] = array(2, 13); // RelatedSoundFile -> ASCII, 13 2675 $tags[0xA005] = array(4, 1); // InteropIFDOffset -> LONG, 1 2676 $tags[0xA20B] = array(5, 1); // FlashEnergy -> RATIONAL, 1 2677 $tags[0xA20C] = array(7, 0); // SpatialFrequencyResponse -> UNDEFINED, Any 2678 $tags[0xA20E] = array(5, 1); // FocalPlaneXResolution -> RATIONAL, 1 2679 $tags[0xA20F] = array(5, 1); // FocalPlaneYResolution -> RATIONAL, 1 2680 $tags[0xA210] = array(3, 1); // FocalPlaneResolutionUnit -> SHORT, 1 2681 $tags[0xA214] = array(3, 2); // SubjectLocation -> SHORT, 2 2682 $tags[0xA215] = array(5, 1); // ExposureIndex -> RATIONAL, 1 2683 $tags[0xA217] = array(3, 1); // SensingMethod -> SHORT, 1 2684 $tags[0xA300] = array(7, 1); // FileSource -> UNDEFINED, 1 2685 $tags[0xA301] = array(7, 1); // SceneType -> UNDEFINED, 1 2686 $tags[0xA302] = array(7, 0); // CFAPattern -> UNDEFINED, Any 2687 } 2688 elseif ($mode == 'interop') { 2689 $tags[0x0001] = array(2, 0); // InteroperabilityIndex -> ASCII, Any 2690 $tags[0x0002] = array(7, 4); // InteroperabilityVersion -> UNKNOWN, 4 2691 $tags[0x1000] = array(2, 0); // RelatedImageFileFormat -> ASCII, Any 2692 $tags[0x1001] = array(4, 1); // RelatedImageWidth -> LONG (or SHORT), 1 2693 $tags[0x1002] = array(4, 1); // RelatedImageLength -> LONG (or SHORT), 1 2694 } 2695 elseif ($mode == 'gps') { 2696 $tags[0x0000] = array(1, 4); // GPSVersionID -> BYTE, 4 2697 $tags[0x0001] = array(2, 2); // GPSLatitudeRef -> ASCII, 2 2698 $tags[0x0002] = array(5, 3); // GPSLatitude -> RATIONAL, 3 2699 $tags[0x0003] = array(2, 2); // GPSLongitudeRef -> ASCII, 2 2700 $tags[0x0004] = array(5, 3); // GPSLongitude -> RATIONAL, 3 2701 $tags[0x0005] = array(2, 2); // GPSAltitudeRef -> ASCII, 2 2702 $tags[0x0006] = array(5, 1); // GPSAltitude -> RATIONAL, 1 2703 $tags[0x0007] = array(5, 3); // GPSTimeStamp -> RATIONAL, 3 2704 $tags[0x0008] = array(2, 0); // GPSSatellites -> ASCII, Any 2705 $tags[0x0009] = array(2, 2); // GPSStatus -> ASCII, 2 2706 $tags[0x000A] = array(2, 2); // GPSMeasureMode -> ASCII, 2 2707 $tags[0x000B] = array(5, 1); // GPSDOP -> RATIONAL, 1 2708 $tags[0x000C] = array(2, 2); // GPSSpeedRef -> ASCII, 2 2709 $tags[0x000D] = array(5, 1); // GPSSpeed -> RATIONAL, 1 2710 $tags[0x000E] = array(2, 2); // GPSTrackRef -> ASCII, 2 2711 $tags[0x000F] = array(5, 1); // GPSTrack -> RATIONAL, 1 2712 $tags[0x0010] = array(2, 2); // GPSImgDirectionRef -> ASCII, 2 2713 $tags[0x0011] = array(5, 1); // GPSImgDirection -> RATIONAL, 1 2714 $tags[0x0012] = array(2, 0); // GPSMapDatum -> ASCII, Any 2715 $tags[0x0013] = array(2, 2); // GPSDestLatitudeRef -> ASCII, 2 2716 $tags[0x0014] = array(5, 3); // GPSDestLatitude -> RATIONAL, 3 2717 $tags[0x0015] = array(2, 2); // GPSDestLongitudeRef -> ASCII, 2 2718 $tags[0x0016] = array(5, 3); // GPSDestLongitude -> RATIONAL, 3 2719 $tags[0x0017] = array(2, 2); // GPSDestBearingRef -> ASCII, 2 2720 $tags[0x0018] = array(5, 1); // GPSDestBearing -> RATIONAL, 1 2721 $tags[0x0019] = array(2, 2); // GPSDestDistanceRef -> ASCII, 2 2722 $tags[0x001A] = array(5, 1); // GPSDestDistance -> RATIONAL, 1 2723 } 2724 2725 return $tags; 2726 } 2727 2728 /*************************************************************/ 2729 function _exifNameTags($mode) 2730 { 2731 $tags = $this->_exifTagNames($mode); 2732 return $this->_names2Tags($tags); 2733 } 2734 2735 /*************************************************************/ 2736 function _iptcTagNames() 2737 { 2738 $tags = array(); 2739 $tags[0x14] = 'SuplementalCategories'; 2740 $tags[0x19] = 'Keywords'; 2741 $tags[0x78] = 'Caption'; 2742 $tags[0x7A] = 'CaptionWriter'; 2743 $tags[0x69] = 'Headline'; 2744 $tags[0x28] = 'SpecialInstructions'; 2745 $tags[0x0F] = 'Category'; 2746 $tags[0x50] = 'Byline'; 2747 $tags[0x55] = 'BylineTitle'; 2748 $tags[0x6E] = 'Credit'; 2749 $tags[0x73] = 'Source'; 2750 $tags[0x74] = 'CopyrightNotice'; 2751 $tags[0x05] = 'ObjectName'; 2752 $tags[0x5A] = 'City'; 2753 $tags[0x5C] = 'Sublocation'; 2754 $tags[0x5F] = 'ProvinceState'; 2755 $tags[0x65] = 'CountryName'; 2756 $tags[0x67] = 'OriginalTransmissionReference'; 2757 $tags[0x37] = 'DateCreated'; 2758 $tags[0x0A] = 'CopyrightFlag'; 2759 2760 return $tags; 2761 } 2762 2763 /*************************************************************/ 2764 function & _iptcNameTags() 2765 { 2766 $tags = $this->_iptcTagNames(); 2767 return $this->_names2Tags($tags); 2768 } 2769 2770 /*************************************************************/ 2771 function _names2Tags($tags2Names) 2772 { 2773 $names2Tags = array(); 2774 reset($tags2Names); 2775 while (list($tag, $name) = each($tags2Names)) { 2776 $names2Tags[$name] = $tag; 2777 } 2778 2779 return $names2Tags; 2780 } 2781 2782 /*************************************************************/ 2783 function _getByte(&$data, $pos) 2784 { 2785 return ord($data{$pos}); 2786 } 2787 2788 /*************************************************************/ 2789 function _putByte(&$data, $pos, $val) 2790 { 2791 $val = intval($val); 2792 2793 $data{$pos} = chr($val); 2794 2795 return $pos + 1; 2796 } 2797 2798 /*************************************************************/ 2799 function _getShort(&$data, $pos, $bigEndian = true) 2800 { 2801 if ($bigEndian) { 2802 return (ord($data{$pos}) << 8) 2803 + ord($data{$pos + 1}); 2804 } 2805 else { 2806 return ord($data{$pos}) 2807 + (ord($data{$pos + 1}) << 8); 2808 } 2809 } 2810 2811 /*************************************************************/ 2812 function _putShort(&$data, $pos = 0, $val, $bigEndian = true) 2813 { 2814 $val = intval($val); 2815 2816 if ($bigEndian) { 2817 $data{$pos + 0} = chr(($val & 0x0000FF00) >> 8); 2818 $data{$pos + 1} = chr(($val & 0x000000FF) >> 0); 2819 } 2820 else { 2821 $data{$pos + 0} = chr(($val & 0x00FF) >> 0); 2822 $data{$pos + 1} = chr(($val & 0xFF00) >> 8); 2823 } 2824 2825 return $pos + 2; 2826 } 2827 2828 /*************************************************************/ 2829 function _getLong(&$data, $pos, $bigEndian = true) 2830 { 2831 if ($bigEndian) { 2832 return (ord($data{$pos}) << 24) 2833 + (ord($data{$pos + 1}) << 16) 2834 + (ord($data{$pos + 2}) << 8) 2835 + ord($data{$pos + 3}); 2836 } 2837 else { 2838 return ord($data{$pos}) 2839 + (ord($data{$pos + 1}) << 8) 2840 + (ord($data{$pos + 2}) << 16) 2841 + (ord($data{$pos + 3}) << 24); 2842 } 2843 } 2844 2845 /*************************************************************/ 2846 function _putLong(&$data, $pos, $val, $bigEndian = true) 2847 { 2848 $val = intval($val); 2849 2850 if ($bigEndian) { 2851 $data{$pos + 0} = chr(($val & 0xFF000000) >> 24); 2852 $data{$pos + 1} = chr(($val & 0x00FF0000) >> 16); 2853 $data{$pos + 2} = chr(($val & 0x0000FF00) >> 8); 2854 $data{$pos + 3} = chr(($val & 0x000000FF) >> 0); 2855 } 2856 else { 2857 $data{$pos + 0} = chr(($val & 0x000000FF) >> 0); 2858 $data{$pos + 1} = chr(($val & 0x0000FF00) >> 8); 2859 $data{$pos + 2} = chr(($val & 0x00FF0000) >> 16); 2860 $data{$pos + 3} = chr(($val & 0xFF000000) >> 24); 2861 } 2862 2863 return $pos + 4; 2864 } 2865 2866 /*************************************************************/ 2867 function & _getNullString(&$data, $pos) 2868 { 2869 $str = ''; 2870 $max = strlen($data); 2871 2872 while ($pos < $max) { 2873 if (ord($data{$pos}) == 0) { 2874 return $str; 2875 } 2876 else { 2877 $str .= $data{$pos}; 2878 } 2879 $pos++; 2880 } 2881 2882 return $str; 2883 } 2884 2885 /*************************************************************/ 2886 function & _getFixedString(&$data, $pos, $length = -1) 2887 { 2888 if ($length == -1) { 2889 $length = strlen($data) - $pos; 2890 } 2891 2892 return substr($data, $pos, $length); 2893 } 2894 2895 /*************************************************************/ 2896 function _putString(&$data, $pos, &$str) 2897 { 2898 $len = strlen($str); 2899 for ($i = 0; $i < $len; $i++) { 2900 $data{$pos + $i} = $str{$i}; 2901 } 2902 2903 return $pos + $len; 2904 } 2905 2906 /*************************************************************/ 2907 function _hexDump(&$data, $start = 0, $length = -1) 2908 { 2909 if (($length == -1) || (($length + $start) > strlen($data))) { 2910 $end = strlen($data); 2911 } 2912 else { 2913 $end = $start + $length; 2914 } 2915 2916 $ascii = ''; 2917 $count = 0; 2918 2919 echo "<tt>\n"; 2920 2921 while ($start < $end) { 2922 if (($count % 16) == 0) { 2923 echo sprintf('%04d', $count) . ': '; 2924 } 2925 2926 $c = ord($data{$start}); 2927 $count++; 2928 $start++; 2929 2930 $aux = dechex($c); 2931 if (strlen($aux) == 1) 2932 echo '0'; 2933 echo $aux . ' '; 2934 2935 if ($c == 60) 2936 $ascii .= '<'; 2937 elseif ($c == 62) 2938 $ascii .= '>'; 2939 elseif ($c == 32) 2940 $ascii .= ' '; 2941 elseif ($c > 32) 2942 $ascii .= chr($c); 2943 else 2944 $ascii .= '.'; 2945 2946 if (($count % 4) == 0) { 2947 echo ' - '; 2948 } 2949 2950 if (($count % 16) == 0) { 2951 echo ': ' . $ascii . "<br>\n"; 2952 $ascii = ''; 2953 } 2954 } 2955 2956 if ($ascii != '') { 2957 while (($count % 16) != 0) { 2958 echo '-- '; 2959 $count++; 2960 if (($count % 4) == 0) { 2961 echo ' - '; 2962 } 2963 } 2964 echo ': ' . $ascii . "<br>\n"; 2965 } 2966 2967 echo "</tt>\n"; 2968 } 2969 2970/*****************************************************************/ 2971} 2972 2973/* vim: set expandtab tabstop=4 shiftwidth=4: */ 2974 2975