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