[ Index ]
 

Code source de DokuWiki 2006-11-06

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/inc/ -> JpegMeta.php (source)

   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  
  31  class 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 .= '&lt;';
2945              elseif ($c == 62)
2946                  $ascii .= '&gt;';
2947              elseif ($c == 32)
2948                  $ascii .= '&nbsp;';
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  


Généré le : Tue Apr 3 20:47:31 2007 par Balluche grâce à PHPXref 0.7