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