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