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