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