Source for file PelIfd.php

Documentation is available at PelIfd.php

  1. <?php
  2.  
  3. /*  PEL: PHP Exif Library.  A library with support for reading and
  4.  *  writing all Exif headers in JPEG and TIFF images using PHP.
  5.  *
  6.  *  Copyright (C) 2004, 2005, 2006, 2007, 2008  Martin Geisler.
  7.  *
  8.  *  This program is free software; you can redistribute it and/or modify
  9.  *  it under the terms of the GNU General Public License as published by
  10.  *  the Free Software Foundation; either version 2 of the License, or
  11.  *  (at your option) any later version.
  12.  *
  13.  *  This program is distributed in the hope that it will be useful,
  14.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *  GNU General Public License for more details.
  17.  *
  18.  *  You should have received a copy of the GNU General Public License
  19.  *  along with this program in the file COPYING; if not, write to the
  20.  *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  21.  *  Boston, MA 02110-1301 USA
  22.  */
  23.  
  24. /* $Id$ */
  25.  
  26.  
  27. /**
  28.  * Classes for dealing with Exif IFDs.
  29.  *
  30.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  31.  * @version $Revision$
  32.  * @date $Date$
  33.  * @license http://www.gnu.org/licenses/gpl.html GNU General Public
  34.  *  License (GPL)
  35.  * @package PEL
  36.  */
  37.  
  38. /**#@+ Required class definitions. */
  39. require_once('PelEntryUndefined.php');
  40. require_once('PelEntryRational.php');
  41. require_once('PelDataWindow.php');
  42. require_once('PelEntryAscii.php');
  43. require_once('PelEntryShort.php');
  44. require_once('PelEntryByte.php');
  45. require_once('PelEntryLong.php');
  46. require_once('PelException.php');
  47. require_once('PelFormat.php');
  48. require_once('PelEntry.php');
  49. require_once('PelTag.php');
  50. require_once('Pel.php');
  51. /**#@-*/
  52.  
  53.  
  54. /**
  55.  * Exception indicating a general problem with the IFD.
  56.  *
  57.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  58.  * @package PEL
  59.  * @subpackage Exception
  60.  */
  61. class PelIfdException extends PelException {}
  62.  
  63. /**
  64.  * Class representing an Image File Directory (IFD).
  65.  *
  66.  * {@link PelTiff TIFF data} is structured as a number of Image File
  67.  * Directories, IFDs for short.  Each IFD contains a number of {@link }
  68.  * PelEntry entries}, some data and finally a link to the next IFD.
  69.  *
  70.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  71.  * @package PEL
  72.  */
  73. class PelIfd implements IteratorAggregateArrayAccess {
  74.  
  75.   /**
  76.    * Main image IFD.
  77.    *
  78.    * Pass this to the constructor when creating an IFD which will be
  79.    * the IFD of the main image.
  80.    */
  81.   const IFD0 = 0;
  82.  
  83.   /**
  84.    * Thumbnail image IFD.
  85.    *
  86.    * Pass this to the constructor when creating an IFD which will be
  87.    * the IFD of the thumbnail image.
  88.    */
  89.   const IFD1 = 1;
  90.  
  91.   /**
  92.    * Exif IFD.
  93.    *
  94.    * Pass this to the constructor when creating an IFD which will be
  95.    * the Exif sub-IFD.
  96.    */
  97.   const EXIF = 2;
  98.  
  99.   /**
  100.    * GPS IFD.
  101.    *
  102.    * Pass this to the constructor when creating an IFD which will be
  103.    * the GPS sub-IFD.
  104.    */
  105.   const GPS  = 3;
  106.  
  107.   /**
  108.    * Interoperability IFD.
  109.    *
  110.    * Pass this to the constructor when creating an IFD which will be
  111.    * the interoperability sub-IFD.
  112.    */
  113.   const INTEROPERABILITY = 4;
  114.  
  115.   /**
  116.    * The entries held by this directory.
  117.    *
  118.    * Each tag in the directory is represented by a {@link PelEntry}
  119.    * object in this array.
  120.    *
  121.    * @var array 
  122.    */
  123.   private $entries array();
  124.  
  125.   /**
  126.    * The type of this directory.
  127.    *
  128.    * Initialized in the constructor.  Must be one of {@link IFD0},
  129.    * {@link IFD1}{@link EXIF}{@link GPS}, or {@link }
  130.    * INTEROPERABILITY}.
  131.    *
  132.    * @var int 
  133.    */
  134.   private $type;
  135.  
  136.   /**
  137.    * The next directory.
  138.    *
  139.    * This will be initialized in the constructor, or be left as null
  140.    * if this is the last directory.
  141.    *
  142.    * @var PelIfd 
  143.    */
  144.   private $next null;
  145.  
  146.   /**
  147.    * Sub-directories pointed to by this directory.
  148.    *
  149.    * This will be an array of ({@link PelTag}{@link PelIfd}) pairs.
  150.    *
  151.    * @var array 
  152.    */
  153.   private $sub array();
  154.  
  155.   /**
  156.    * The thumbnail data.
  157.    *
  158.    * This will be initialized in the constructor, or be left as null
  159.    * if there are no thumbnail as part of this directory.
  160.    *
  161.    * @var PelDataWindow 
  162.    */
  163.   private $thumb_data null;
  164.   // TODO: use this format to choose between the
  165.   // JPEG_INTERCHANGE_FORMAT and STRIP_OFFSETS tags.
  166.   // private $thumb_format;
  167.  
  168.   
  169.   /**
  170.    * Construct a new Image File Directory (IFD).
  171.    *
  172.    * The IFD will be empty, use the {@link addEntry()} method to add
  173.    * an {@link PelEntry}.  Use the {@link setNext()} method to link
  174.    * this IFD to another.
  175.    *
  176.    * @param int type the type of this IFD.  Must be one of {@link }
  177.    *  IFD0}, {@link IFD1}{@link EXIF}{@link GPS}, or {@link }
  178.    *  INTEROPERABILITY}.  An {@link PelIfdException} will be thrown
  179.    *  otherwise.
  180.    */
  181.   function __construct($type{
  182.     if ($type != PelIfd::IFD0 && $type != PelIfd::IFD1 &&
  183.         $type != PelIfd::EXIF && $type != PelIfd::GPS &&
  184.         $type != PelIfd::INTEROPERABILITY)
  185.       throw new PelIfdException('Unknown IFD type: %d'$type);
  186.  
  187.     $this->type $type;
  188.   }
  189.  
  190.  
  191.   /**
  192.    * Load data into a Image File Directory (IFD).
  193.    *
  194.    * @param PelDataWindow the data window that will provide the data.
  195.    *
  196.    * @param int the offset within the window where the directory will
  197.    *  be found.
  198.    */
  199.   function load(PelDataWindow $d$offset{
  200.     $thumb_offset 0;
  201.     $thumb_length 0;
  202.  
  203.     Pel::debug('Constructing IFD at offset %d from %d bytes...',
  204.                $offset$d->getSize());
  205.  
  206.     /* Read the number of entries */
  207.     $n $d->getShort($offset);
  208.     Pel::debug('Loading %d entries...'$n);
  209.     
  210.     $offset += 2;
  211.  
  212.     /* Check if we have enough data. */
  213.     if ($offset 12 $n $d->getSize()) {
  214.       $n floor(($offset $d->getSize()) 12);
  215.       Pel::maybeThrow(new PelIfdException('Adjusted to: %d.'$n));
  216.     }
  217.  
  218.     for ($i 0$i $n$i++{
  219.       // TODO: increment window start instead of using offsets.
  220.       $tag $d->getShort($offset 12 $i);
  221.       Pel::debug('Loading entry with tag 0x%04X: %s (%d of %d)...',
  222.                  $tagPelTag::getName($this->type$tag)$i 1$n);
  223.       
  224.       switch ($tag{
  225.       case PelTag::EXIF_IFD_POINTER:
  226.       case PelTag::GPS_INFO_IFD_POINTER:
  227.       case PelTag::INTEROPERABILITY_IFD_POINTER:
  228.         $o $d->getLong($offset 12 $i 8);
  229.         Pel::debug('Found sub IFD at offset %d'$o);
  230.  
  231.         /* Map tag to IFD type. */
  232.         if ($tag == PelTag::EXIF_IFD_POINTER)
  233.           $type PelIfd::EXIF;
  234.         elseif ($tag == PelTag::GPS_INFO_IFD_POINTER)
  235.           $type PelIfd::GPS;
  236.         elseif ($tag == PelTag::INTEROPERABILITY_IFD_POINTER)
  237.           $type PelIfd::INTEROPERABILITY;
  238.  
  239.         $this->sub[$typenew PelIfd($type);
  240.         $this->sub[$type]->load($d$o);
  241.         break;
  242.       case PelTag::JPEG_INTERCHANGE_FORMAT:
  243.         $thumb_offset $d->getLong($offset 12 $i 8);
  244.         $this->safeSetThumbnail($d$thumb_offset$thumb_length);
  245.         break;
  246.       case PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH:
  247.         $thumb_length $d->getLong($offset 12 $i 8);
  248.         $this->safeSetThumbnail($d$thumb_offset$thumb_length);
  249.         break;
  250.       default:
  251.         $format     $d->getShort($offset 12 $i 2);
  252.         $components $d->getLong($offset 12 $i 4);
  253.         
  254.         /* The data size.  If bigger than 4 bytes, the actual data is
  255.          * not in the entry but somewhere else, with the offset stored
  256.          * in the entry.
  257.          */
  258.         $s PelFormat::getSize($format$components;
  259.         if ($s 0{    
  260.           $doff $offset 12 $i 8;
  261.           if ($s 4)
  262.             $doff $d->getLong($doff);
  263.  
  264.           $data $d->getClone($doff$s);
  265.         else {
  266.           $data new PelDataWindow();
  267.         }
  268.  
  269.         try {
  270.           $entry $this->newEntryFromData($tag$format$components$data);
  271.           $this->addEntry($entry);
  272.         catch (PelException $e{
  273.           /* Throw the exception when running in strict mode, store
  274.            * otherwise. */
  275.           Pel::maybeThrow($e);
  276.         }
  277.  
  278.         /* The format of the thumbnail is stored in this tag. */
  279. //         TODO: handle TIFF thumbnail.
  280. //         if ($tag == PelTag::COMPRESSION) {
  281. //           $this->thumb_format = $data->getShort();
  282. //         }
  283.         break;
  284.       }
  285.     }
  286.  
  287.     /* Offset to next IFD */
  288.     $o $d->getLong($offset 12 $n);
  289.     Pel::debug('Current offset is %d, link at %d points to %d.',
  290.                $offset,  $offset 12 $n$o);
  291.  
  292.     if ($o 0{
  293.       /* Sanity check: we need 6 bytes  */
  294.       if ($o $d->getSize(6{
  295.         Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: ' .
  296.                                             '%d > %d!',
  297.                                             $o$d->getSize(6));
  298.       else {
  299.         if ($this->type == PelIfd::IFD1// IFD1 shouldn't link further...
  300.           Pel::maybeThrow(new PelIfdException('IFD1 links to another IFD!'));
  301.  
  302.         $this->next new PelIfd(PelIfd::IFD1);
  303.         $this->next->load($d$o);
  304.       }
  305.     else {
  306.       Pel::debug('Last IFD.');
  307.     }
  308.   }
  309.  
  310.  
  311.   /**
  312.    * Make a new entry from a bunch of bytes.
  313.    *
  314.    * This method will create the proper subclass of {@link PelEntry}
  315.    * corresponding to the {@link PelTag} and {@link PelFormat} given.
  316.    * The entry will be initialized with the data given.
  317.    *
  318.    * Please note that the data you pass to this method should come
  319.    * from an image, that is, it should be raw bytes.  If instead you
  320.    * want to create an entry for holding, say, an short integer, then
  321.    * create a {@link PelEntryShort} object directly and load the data
  322.    * into it.
  323.    *
  324.    * A {@link PelUnexpectedFormatException} is thrown if a mismatch is
  325.    * discovered between the tag and format, and likewise a {@link }
  326.    * PelWrongComponentCountException} is thrown if the number of
  327.    * components does not match the requirements of the tag.  The
  328.    * requirements for a given tag (if any) can be found in the
  329.    * documentation for {@link PelTag}.
  330.    *
  331.    * @param PelTag the tag of the entry.
  332.    *
  333.    * @param PelFormat the format of the entry.
  334.    *
  335.    * @param int the components in the entry.
  336.    *
  337.    * @param PelDataWindow the data which will be used to construct the
  338.    *  entry.
  339.    *
  340.    * @return PelEntry a newly created entry, holding the data given.
  341.    */
  342.   function newEntryFromData($tag$format$componentsPelDataWindow $data{
  343.  
  344.     /* First handle tags for which we have a specific PelEntryXXX
  345.      * class. */
  346.  
  347.     switch ($this->type{
  348.  
  349.     case self::IFD0:
  350.     case self::IFD1:
  351.     case self::EXIF:
  352.     case self::INTEROPERABILITY:
  353.  
  354.       switch ($tag{
  355.       case PelTag::DATE_TIME:
  356.       case PelTag::DATE_TIME_ORIGINAL:
  357.       case PelTag::DATE_TIME_DIGITIZED:
  358.         if ($format != PelFormat::ASCII)
  359.           throw new PelUnexpectedFormatException($this->type$tag$format,
  360.                                                PelFormat::ASCII);
  361.  
  362.         if ($components != 20)
  363.           throw new PelWrongComponentCountException($this->type$tag$components20);
  364.  
  365.         // TODO: handle timezones.
  366.         return new PelEntryTime($tag$data->getBytes(0-1)PelEntryTime::EXIF_STRING);
  367.  
  368.       case PelTag::COPYRIGHT:
  369.         if ($format != PelFormat::ASCII)
  370.           throw new PelUnexpectedFormatException($this->type$tag$format,
  371.                                                  PelFormat::ASCII);
  372.  
  373.         $v explode("\0"trim($data->getBytes()' '));
  374.         return new PelEntryCopyright($v[0]$v[1]);
  375.  
  376.       case PelTag::EXIF_VERSION:
  377.       case PelTag::FLASH_PIX_VERSION:
  378.       case PelTag::INTEROPERABILITY_VERSION:
  379.         if ($format != PelFormat::UNDEFINED)
  380.           throw new PelUnexpectedFormatException($this->type$tag$format,
  381.                                                PelFormat::UNDEFINED);
  382.  
  383.         return new PelEntryVersion($tag$data->getBytes(100);
  384.  
  385.       case PelTag::USER_COMMENT:
  386.         if ($format != PelFormat::UNDEFINED)
  387.           throw new PelUnexpectedFormatException($this->type$tag$format,
  388.                                                  PelFormat::UNDEFINED);
  389.         if ($data->getSize(8{
  390.           return new PelEntryUserComment();
  391.         else {
  392.           return new PelEntryUserComment($data->getBytes(8),
  393.                                        rtrim($data->getBytes(08)));
  394.         }
  395.  
  396.       case PelTag::XP_TITLE:
  397.       case PelTag::XP_COMMENT:
  398.       case PelTag::XP_AUTHOR:
  399.       case PelTag::XP_KEYWORDS:
  400.       case PelTag::XP_SUBJECT:
  401.         if ($format != PelFormat::BYTE)
  402.           throw new PelUnexpectedFormatException($this->type$tag$format,
  403.                                                PelFormat::BYTE);
  404.  
  405.         $v '';
  406.         for ($i 0$i $components$i++{
  407.           $b $data->getByte($i);
  408.           /* Convert the byte to a character if it is non-null ---
  409.            * information about the character encoding of these entries
  410.            * would be very nice to have!  So far my tests have shown
  411.            * that characters in the Latin-1 character set are stored in
  412.            * a single byte followed by a NULL byte. */
  413.           if ($b != 0)
  414.             $v .= chr($b);
  415.         }
  416.  
  417.         return new PelEntryWindowsString($tag$v);
  418.       }
  419.  
  420.     case self::GPS:
  421.       
  422.     default:
  423.       /* Then handle the basic formats. */
  424.       switch ($format{
  425.       case PelFormat::BYTE:
  426.         $v =  new PelEntryByte($tag);
  427.         for ($i 0$i $components$i++)
  428.           $v->addNumber($data->getByte($i));
  429.         return $v;
  430.  
  431.       case PelFormat::SBYTE:
  432.         $v =  new PelEntrySByte($tag);
  433.         for ($i 0$i $components$i++)
  434.           $v->addNumber($data->getSByte($i));
  435.         return $v;
  436.  
  437.       case PelFormat::ASCII:
  438.         return new PelEntryAscii($tag$data->getBytes(0-1));
  439.  
  440.       case PelFormat::SHORT:
  441.         $v =  new PelEntryShort($tag);
  442.         for ($i 0$i $components$i++)
  443.           $v->addNumber($data->getShort($i*2));
  444.         return $v;
  445.  
  446.       case PelFormat::SSHORT:
  447.         $v =  new PelEntrySShort($tag);
  448.         for ($i 0$i $components$i++)
  449.           $v->addNumber($data->getSShort($i*2));
  450.         return $v;
  451.  
  452.       case PelFormat::LONG:
  453.         $v =  new PelEntryLong($tag);
  454.         for ($i 0$i $components$i++)
  455.           $v->addNumber($data->getLong($i*4));
  456.         return $v;
  457.  
  458.       case PelFormat::SLONG:
  459.         $v =  new PelEntrySLong($tag);
  460.         for ($i 0$i $components$i++)
  461.           $v->addNumber($data->getSLong($i*4));
  462.         return $v;
  463.  
  464.       case PelFormat::RATIONAL:
  465.         $v =  new PelEntryRational($tag);
  466.         for ($i 0$i $components$i++)
  467.           $v->addNumber($data->getRational($i*8));
  468.         return $v;
  469.  
  470.       case PelFormat::SRATIONAL:
  471.         $v =  new PelEntrySRational($tag);
  472.         for ($i 0$i $components$i++)
  473.           $v->addNumber($data->getSRational($i*8));
  474.         return $v;
  475.  
  476.       case PelFormat::UNDEFINED:
  477.         return new PelEntryUndefined($tag$data->getBytes());
  478.  
  479.       default:
  480.         throw new PelException('Unsupported format: %s',
  481.                                PelFormat::getName($format));
  482.       }
  483.     }
  484.   }
  485.  
  486.  
  487.  
  488.  
  489.   /**
  490.    * Extract thumbnail data safely.
  491.    *
  492.    * It is safe to call this method repeatedly with either the offset
  493.    * or the length set to zero, since it requires both of these
  494.    * arguments to be positive before the thumbnail is extracted.
  495.    *
  496.    * When both parameters are set it will check the length against the
  497.    * available data and adjust as necessary. Only then is the
  498.    * thumbnail data loaded.
  499.    *
  500.    * @param PelDataWindow the data from which the thumbnail will be
  501.    *  extracted.
  502.    *
  503.    * @param int the offset into the data.
  504.    *
  505.    * @param int the length of the thumbnail.
  506.    */
  507.   private function safeSetThumbnail(PelDataWindow $d$offset$length{
  508.     /* Load the thumbnail if both the offset and the length is
  509.      * available. */
  510.     if ($offset && $length 0{
  511.       /* Some images have a broken length, so we try to carefully
  512.        * check the length before we store the thumbnail. */
  513.       if ($offset $length $d->getSize()) {
  514.         Pel::maybeThrow(new PelIfdException('Thumbnail length %d bytes ' .
  515.                                             'adjusted to %d bytes.',
  516.                                             $length,
  517.                                             $d->getSize($offset));
  518.         $length $d->getSize($offset;
  519.       }
  520.  
  521.       /* Now set the thumbnail normally. */
  522.       $this->setThumbnail($d->getClone($offset$length));
  523.     }
  524.   }
  525.  
  526.   
  527.   /**
  528.    * Set thumbnail data.
  529.    *
  530.    * Use this to embed an arbitrary JPEG image within this IFD. The
  531.    * data will be checked to ensure that it has a proper {@link }
  532.    * PelJpegMarker::EOI} at the end.  If not, then the length is
  533.    * adjusted until one if found.  An {@link PelIfdException} might be
  534.    * thrown (depending on {@link Pel::$strict}) this case.
  535.    *
  536.    * @param PelDataWindow the thumbnail data.
  537.    */
  538.   function setThumbnail(PelDataWindow $d{
  539.     $size $d->getSize();
  540.     /* Now move backwards until we find the EOI JPEG marker. */
  541.     while ($d->getByte($size 2!= 0xFF ||
  542.            $d->getByte($size 1!= PelJpegMarker::EOI{
  543.       $size--;
  544.     }
  545.  
  546.     if ($size != $d->getSize())
  547.       Pel::maybeThrow(new PelIfdException('Decrementing thumbnail size ' .
  548.                                           'to %d bytes'$size));
  549.     
  550.     $this->thumb_data $d->getClone(0$size);
  551.   }
  552.  
  553.  
  554.   /**
  555.    * Get the type of this directory.
  556.    *
  557.    * @return int of {@link PelIfd::IFD0}{@link PelIfd::IFD1}{@link }
  558.    *  PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link }
  559.    *  PelIfd::INTEROPERABILITY}.
  560.    */
  561.   function getType({
  562.     return $this->type;
  563.   }
  564.  
  565.  
  566.   /**
  567.    * Is a given tag valid for this IFD?
  568.    *
  569.    * Different types of IFDs can contain different kinds of tags ---
  570.    * the {@link IFD0} type, for example, cannot contain a {@link }
  571.    * PelTag::GPS_LONGITUDE} tag.
  572.    *
  573.    * A special exception is tags with values above 0xF000.  They are
  574.    * treated as private tags and will be allowed everywhere (use this
  575.    * for testing or for implementing your own types of tags).
  576.    *
  577.    * @param PelTag the tag.
  578.    *
  579.    * @return boolean true if the tag is considered valid in this IFD,
  580.    *  false otherwise.
  581.    *
  582.    * @see getValidTags()
  583.    */
  584.   function isValidTag($tag{
  585.     return $tag 0xF000 || in_array($tag$this->getValidTags());
  586.   }
  587.  
  588.  
  589.   /**
  590.    * Returns a list of valid tags for this IFD.
  591.    *
  592.    * @return array an array of {@link PelTag}s which are valid for
  593.    *  this IFD.
  594.    */
  595.   function getValidTags({
  596.     switch ($this->type{
  597.     case PelIfd::IFD0:
  598.     case PelIfd::IFD1:
  599.       return array(PelTag::IMAGE_WIDTH,
  600.                    PelTag::IMAGE_LENGTH,
  601.                    PelTag::BITS_PER_SAMPLE,
  602.                    PelTag::COMPRESSION,
  603.                    PelTag::PHOTOMETRIC_INTERPRETATION,
  604.                    PelTag::IMAGE_DESCRIPTION,
  605.                    PelTag::MAKE,
  606.                    PelTag::MODEL,
  607.                    PelTag::STRIP_OFFSETS,
  608.                    PelTag::ORIENTATION,
  609.                    PelTag::SAMPLES_PER_PIXEL,
  610.                    PelTag::ROWS_PER_STRIP,
  611.                    PelTag::STRIP_BYTE_COUNTS,
  612.                    PelTag::X_RESOLUTION,
  613.                    PelTag::Y_RESOLUTION,
  614.                    PelTag::PLANAR_CONFIGURATION,
  615.                    PelTag::RESOLUTION_UNIT,
  616.                    PelTag::TRANSFER_FUNCTION,
  617.                    PelTag::SOFTWARE,
  618.                    PelTag::DATE_TIME,
  619.                    PelTag::ARTIST,
  620.                    PelTag::WHITE_POINT,
  621.                    PelTag::PRIMARY_CHROMATICITIES,
  622.                    PelTag::JPEG_INTERCHANGE_FORMAT,
  623.                    PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH,
  624.                    PelTag::YCBCR_COEFFICIENTS,
  625.                    PelTag::YCBCR_SUB_SAMPLING,
  626.                    PelTag::YCBCR_POSITIONING,
  627.                    PelTag::REFERENCE_BLACK_WHITE,
  628.                    PelTag::COPYRIGHT,
  629.                    PelTag::EXIF_IFD_POINTER,
  630.                    PelTag::GPS_INFO_IFD_POINTER,
  631.                    PelTag::PRINT_IM,
  632.                    PelTag::XP_TITLE,
  633.                    PelTag::XP_COMMENT,
  634.                    PelTag::XP_AUTHOR,
  635.                    PelTag::XP_KEYWORDS,
  636.                    PelTag::XP_SUBJECT);
  637.  
  638.     case PelIfd::EXIF:
  639.       return array(PelTag::EXPOSURE_TIME,
  640.                    PelTag::FNUMBER,
  641.                    PelTag::EXPOSURE_PROGRAM,
  642.                    PelTag::SPECTRAL_SENSITIVITY,
  643.                    PelTag::ISO_SPEED_RATINGS,
  644.                    PelTag::OECF,
  645.                    PelTag::EXIF_VERSION,
  646.                    PelTag::DATE_TIME_ORIGINAL,
  647.                    PelTag::DATE_TIME_DIGITIZED,
  648.                    PelTag::COMPONENTS_CONFIGURATION,
  649.                    PelTag::COMPRESSED_BITS_PER_PIXEL,
  650.                    PelTag::SHUTTER_SPEED_VALUE,
  651.                    PelTag::APERTURE_VALUE,
  652.                    PelTag::BRIGHTNESS_VALUE,
  653.                    PelTag::EXPOSURE_BIAS_VALUE,
  654.                    PelTag::MAX_APERTURE_VALUE,
  655.                    PelTag::SUBJECT_DISTANCE,
  656.                    PelTag::METERING_MODE,
  657.                    PelTag::LIGHT_SOURCE,
  658.                    PelTag::FLASH,
  659.                    PelTag::FOCAL_LENGTH,
  660.                    PelTag::MAKER_NOTE,
  661.                    PelTag::USER_COMMENT,
  662.                    PelTag::SUB_SEC_TIME,
  663.                    PelTag::SUB_SEC_TIME_ORIGINAL,
  664.                    PelTag::SUB_SEC_TIME_DIGITIZED,
  665.                    PelTag::FLASH_PIX_VERSION,
  666.                    PelTag::COLOR_SPACE,
  667.                    PelTag::PIXEL_X_DIMENSION,
  668.                    PelTag::PIXEL_Y_DIMENSION,
  669.                    PelTag::RELATED_SOUND_FILE,
  670.                    PelTag::FLASH_ENERGY,
  671.                    PelTag::SPATIAL_FREQUENCY_RESPONSE,
  672.                    PelTag::FOCAL_PLANE_X_RESOLUTION,
  673.                    PelTag::FOCAL_PLANE_Y_RESOLUTION,
  674.                    PelTag::FOCAL_PLANE_RESOLUTION_UNIT,
  675.                    PelTag::SUBJECT_LOCATION,
  676.                    PelTag::EXPOSURE_INDEX,
  677.                    PelTag::SENSING_METHOD,
  678.                    PelTag::FILE_SOURCE,
  679.                    PelTag::SCENE_TYPE,
  680.                    PelTag::CFA_PATTERN,
  681.                    PelTag::CUSTOM_RENDERED,
  682.                    PelTag::EXPOSURE_MODE,
  683.                    PelTag::WHITE_BALANCE,
  684.                    PelTag::DIGITAL_ZOOM_RATIO,
  685.                    PelTag::FOCAL_LENGTH_IN_35MM_FILM,
  686.                    PelTag::SCENE_CAPTURE_TYPE,
  687.                    PelTag::GAIN_CONTROL,
  688.                    PelTag::CONTRAST,
  689.                    PelTag::SATURATION,
  690.                    PelTag::SHARPNESS,
  691.                    PelTag::DEVICE_SETTING_DESCRIPTION,
  692.                    PelTag::SUBJECT_DISTANCE_RANGE,
  693.                    PelTag::IMAGE_UNIQUE_ID,
  694.                    PelTag::INTEROPERABILITY_IFD_POINTER,
  695.                    PelTag::GAMMA);
  696.  
  697.     case PelIfd::GPS:
  698.       return array(PelTag::GPS_VERSION_ID
  699.                    PelTag::GPS_LATITUDE_REF
  700.                    PelTag::GPS_LATITUDE
  701.                    PelTag::GPS_LONGITUDE_REF
  702.                    PelTag::GPS_LONGITUDE
  703.                    PelTag::GPS_ALTITUDE_REF,
  704.                    PelTag::GPS_ALTITUDE,
  705.                    PelTag::GPS_TIME_STAMP,
  706.                    PelTag::GPS_SATELLITES,
  707.                    PelTag::GPS_STATUS,
  708.                    PelTag::GPS_MEASURE_MODE,
  709.                    PelTag::GPS_DOP,
  710.                    PelTag::GPS_SPEED_REF,
  711.                    PelTag::GPS_SPEED,
  712.                    PelTag::GPS_TRACK_REF,
  713.                    PelTag::GPS_TRACK,
  714.                    PelTag::GPS_IMG_DIRECTION_REF,
  715.                    PelTag::GPS_IMG_DIRECTION,
  716.                    PelTag::GPS_MAP_DATUM,
  717.                    PelTag::GPS_DEST_LATITUDE_REF,
  718.                    PelTag::GPS_DEST_LATITUDE,
  719.                    PelTag::GPS_DEST_LONGITUDE_REF,
  720.                    PelTag::GPS_DEST_LONGITUDE,
  721.                    PelTag::GPS_DEST_BEARING_REF,
  722.                    PelTag::GPS_DEST_BEARING,
  723.                    PelTag::GPS_DEST_DISTANCE_REF,
  724.                    PelTag::GPS_DEST_DISTANCE,
  725.                    PelTag::GPS_PROCESSING_METHOD,
  726.                    PelTag::GPS_AREA_INFORMATION,
  727.                    PelTag::GPS_DATE_STAMP,
  728.                    PelTag::GPS_DIFFERENTIAL);
  729.  
  730.     case PelIfd::INTEROPERABILITY:
  731.       return array(PelTag::INTEROPERABILITY_INDEX
  732.                    PelTag::INTEROPERABILITY_VERSION,
  733.                    PelTag::RELATED_IMAGE_FILE_FORMAT
  734.                    PelTag::RELATED_IMAGE_WIDTH
  735.                    PelTag::RELATED_IMAGE_LENGTH);
  736.  
  737.       /* TODO: Where do these tags belong?
  738. PelTag::FILL_ORDER,
  739. PelTag::DOCUMENT_NAME, 
  740. PelTag::TRANSFER_RANGE, 
  741. PelTag::JPEG_PROC, 
  742. PelTag::BATTERY_LEVEL, 
  743. PelTag::IPTC_NAA, 
  744. PelTag::INTER_COLOR_PROFILE, 
  745. PelTag::CFA_REPEAT_PATTERN_DIM, 
  746.       */
  747.     }
  748.   }
  749.  
  750.  
  751.   /**
  752.    * Get the name of an IFD type.
  753.    *
  754.    * @param int one of {@link PelIfd::IFD0}{@link PelIfd::IFD1},
  755.    *  {@link PelIfd::EXIF}{@link PelIfd::GPS}, or {@link }
  756.    *  PelIfd::INTEROPERABILITY}.
  757.    *
  758.    * @return string the name of type.
  759.    */
  760.   static function getTypeName($type{
  761.     switch ($type{
  762.     case self::IFD0:
  763.       return '0';
  764.     case self::IFD1:
  765.       return '1';
  766.     case self::EXIF:
  767.       return 'Exif';
  768.     case self::GPS:
  769.       return 'GPS';
  770.     case self::INTEROPERABILITY:
  771.       return 'Interoperability';
  772.     default:
  773.       throw new PelIfdException('Unknown IFD type: %d'$type);
  774.     }
  775.   }
  776.  
  777.  
  778.   /**
  779.    * Get the name of this directory.
  780.    *
  781.    * @return string the name of this directory.
  782.    */
  783.   function getName({
  784.     return $this->getTypeName($this->type);
  785.   }
  786.  
  787.  
  788.   /**
  789.    * Adds an entry to the directory.
  790.    *
  791.    * @param PelEntry the entry that will be added. If the entry is not
  792.    *  valid in this IFD (as per {@link isValidTag()}) an
  793.    *  {@link PelInvalidDataException} is thrown.
  794.    *
  795.    * @todo The entry will be identified with its tag, so each
  796.    *  directory can only contain one entry with each tag.  Is this a
  797.    *  bug?
  798.    */
  799.   function addEntry(PelEntry $e{
  800.     if ($this->isValidTag($e->getTag())) {
  801.       $e->setIfdType($this->type);
  802.       $this->entries[$e->getTag()$e;
  803.     else {
  804.       throw new PelInvalidDataException("IFD %s cannot hold\n%s",
  805.                                         $this->getName()$e->__toString());
  806.     }
  807.   }
  808.  
  809.  
  810.   /**
  811.    * Does a given tag exist in this IFD?
  812.    *
  813.    * This methods is part of the ArrayAccess SPL interface for
  814.    * overriding array access of objects, it allows you to check for
  815.    * existance of an entry in the IFD:
  816.    *
  817.    * <code>
  818.    * if (isset($ifd[PelTag::FNUMBER]))
  819.    *   // ... do something with the F-number.
  820.    * </code>
  821.    *
  822.    * @param PelTag the offset to check.
  823.    *
  824.    * @return boolean whether the tag exists.
  825.    */
  826.   function offsetExists($tag{
  827.     return isset($this->entries[$tag]);
  828.   }
  829.  
  830.  
  831.   /**
  832.    * Retrieve a given tag from this IFD.
  833.    *
  834.    * This methods is part of the ArrayAccess SPL interface for
  835.    * overriding array access of objects, it allows you to read entries
  836.    * from the IFD the same was as for an array:
  837.    *
  838.    * <code>
  839.    * $entry = $ifd[PelTag::FNUMBER];
  840.    * </code>
  841.    *
  842.    * @param PelTag the tag to return.  It is an error to ask for a tag
  843.    *  which is not in the IFD, just like asking for a non-existant
  844.    *  array entry.
  845.    *
  846.    * @return PelEntry the entry.
  847.    */
  848.   function offsetGet($tag{
  849.     return $this->entries[$tag];
  850.   }
  851.  
  852.  
  853.   /**
  854.    * Set or update a given tag in this IFD.
  855.    *
  856.    * This methods is part of the ArrayAccess SPL interface for
  857.    * overriding array access of objects, it allows you to add new
  858.    * entries or replace esisting entries by doing:
  859.    *
  860.    * <code>
  861.    * $ifd[PelTag::EXPOSURE_BIAS_VALUE] = $entry;
  862.    * </code>
  863.    *
  864.    * Note that the actual array index passed is ignored!  Instead the
  865.    * {@link PelTag} from the entry is used.
  866.    *
  867.    * @param PelTag the offset to update.
  868.    *
  869.    * @param PelEntry the new value.
  870.    */
  871.   function offsetSet($tag$e{
  872.     if ($e instanceof PelEntry{
  873.       $tag $e->getTag();
  874.       $this->entries[$tag$e;
  875.     else {
  876.       throw new PelInvalidArgumentException('Argument "%s" must be a PelEntry.'$e);
  877.     }
  878.   }
  879.  
  880.  
  881.   /**
  882.    * Unset a given tag in this IFD.
  883.    *
  884.    * This methods is part of the ArrayAccess SPL interface for
  885.    * overriding array access of objects, it allows you to delete
  886.    * entries in the IFD by doing:
  887.    *
  888.    * <code>
  889.    * unset($ifd[PelTag::EXPOSURE_BIAS_VALUE])
  890.    * </code>
  891.    *
  892.    * @param PelTag the offset to delete.
  893.    */
  894.   function offsetUnset($tag{
  895.     unset($this->entries[$tag]);
  896.   }
  897.  
  898.  
  899.   /**
  900.    * Retrieve an entry.
  901.    *
  902.    * @param PelTag the tag identifying the entry.
  903.    *
  904.    * @return PelEntry the entry associated with the tag, or null if no
  905.    *  such entry exists.
  906.    */
  907.   function getEntry($tag{
  908.     if (isset($this->entries[$tag]))
  909.       return $this->entries[$tag];
  910.     else
  911.       return null;
  912.   }
  913.  
  914.  
  915.   /**
  916.    * Returns all entries contained in this IFD.
  917.    *
  918.    * @return array an array of {@link PelEntry} objects, or rather
  919.    *  descendant classes.  The array has {@link PelTag}s as keys
  920.    *  and the entries as values.
  921.    *
  922.    * @see getEntry
  923.    * @see getIterator
  924.    */
  925.   function getEntries({
  926.     return $this->entries;
  927.   }
  928.  
  929.   
  930.   /**
  931.    * Return an iterator for all entries contained in this IFD.
  932.    *
  933.    * Used with foreach as in
  934.    *
  935.    * <code>
  936.    * foreach ($ifd as $tag => $entry) {
  937.    *   // $tag is now a PelTag and $entry is a PelEntry object.
  938.    * }
  939.    * </code>
  940.    *
  941.    * @return Iterator an iterator using the {@link PelTag tags} as
  942.    *  keys and the entries as values.
  943.    */
  944.   function getIterator({
  945.     return new ArrayIterator($this->entries);
  946.   }
  947.   
  948.  
  949.   /**
  950.    * Returns available thumbnail data.
  951.    *
  952.    * @return string the bytes in the thumbnail, if any.  If the IFD
  953.    *  does not contain any thumbnail data, the empty string is
  954.    *  returned.
  955.    *
  956.    * @todo Throw an exception instead when no data is available?
  957.    *
  958.    * @todo Return the $this->thumb_data object instead of the bytes?
  959.    */
  960.   function getThumbnailData({
  961.     if ($this->thumb_data != null)
  962.       return $this->thumb_data->getBytes();
  963.     else
  964.       return '';
  965.   }
  966.   
  967.  
  968.   /**
  969.    * Make this directory point to a new directory.
  970.    *
  971.    * @param PelIfd the IFD that this directory will point to.
  972.    */
  973.   function setNextIfd(PelIfd $i{
  974.     $this->next $i;
  975.   }
  976.  
  977.  
  978.   /**
  979.    * Return the IFD pointed to by this directory.
  980.    *
  981.    * @return PelIfd the next IFD, following this IFD. If this is the
  982.    *  last IFD, null is returned.
  983.    */
  984.   function getNextIfd({
  985.     return $this->next;
  986.   }
  987.  
  988.  
  989.   /**
  990.    * Check if this is the last IFD.
  991.    *
  992.    * @return boolean true if there are no following IFD, false
  993.    *  otherwise.
  994.    */
  995.   function isLastIfd({
  996.     return $this->next == null;
  997.   }
  998.  
  999.  
  1000.   /**
  1001.    * Add a sub-IFD.
  1002.    *
  1003.    * Any previous sub-IFD of the same type will be overwritten.
  1004.    *
  1005.    * @param PelIfd the sub IFD.  The type of must be one of {@link }
  1006.    *  PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link }
  1007.    *  PelIfd::INTEROPERABILITY}.
  1008.    */
  1009.   function addSubIfd(PelIfd $sub{
  1010.     $this->sub[$sub->type$sub;
  1011.   }
  1012.  
  1013.  
  1014.   /**
  1015.    * Return a sub IFD.
  1016.    *
  1017.    * @param int the type of the sub IFD.  This must be one of {@link }
  1018.    *  PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link }
  1019.    *  PelIfd::INTEROPERABILITY}.
  1020.    *
  1021.    * @return PelIfd the IFD associated with the type, or null if that
  1022.    *  sub IFD does not exist.
  1023.    */
  1024.   function getSubIfd($type{
  1025.     if (isset($this->sub[$type]))
  1026.       return $this->sub[$type];
  1027.     else
  1028.       return null;
  1029.   }
  1030.  
  1031.  
  1032.   /**
  1033.    * Get all sub IFDs.
  1034.    *
  1035.    * @return array an associative array with (IFD-type, {@link }
  1036.    *  PelIfd}) pairs.
  1037.    */
  1038.   function getSubIfds({
  1039.     return $this->sub;
  1040.   }
  1041.  
  1042.  
  1043.   /**
  1044.    * Turn this directory into bytes.
  1045.    *
  1046.    * This directory will be turned into a byte string, with the
  1047.    * specified byte order.  The offsets will be calculated from the
  1048.    * offset given.
  1049.    *
  1050.    * @param int the offset of the first byte of this directory.
  1051.    *
  1052.    * @param PelByteOrder the byte order that should be used when
  1053.    *  turning integers into bytes.  This should be one of {@link }
  1054.    *  PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
  1055.    */
  1056.   function getBytes($offset$order{
  1057.     $bytes '';
  1058.     $extra_bytes '';
  1059.  
  1060.     Pel::debug('Bytes from IDF will start at offset %d within Exif data',
  1061.                $offset);
  1062.     
  1063.     $n count($this->entriescount($this->sub);
  1064.     if ($this->thumb_data != null{
  1065.       /* We need two extra entries for the thumbnail offset and
  1066.        * length. */
  1067.       $n += 2;
  1068.     }
  1069.  
  1070.     $bytes .= PelConvert::shortToBytes($n$order);
  1071.  
  1072.     /* Initialize offset of extra data.  This included the bytes
  1073.      * preceding this IFD, the bytes needed for the count of entries,
  1074.      * the entries themselves (and sub entries), the extra data in the
  1075.      * entries, and the IFD link.
  1076.      */
  1077.     $end $offset 12 $n 4;
  1078.  
  1079.     foreach ($this->entries as $tag => $entry{
  1080.       /* Each entry is 12 bytes long. */
  1081.       $bytes .= PelConvert::shortToBytes($entry->getTag()$order);
  1082.       $bytes .= PelConvert::shortToBytes($entry->getFormat()$order);
  1083.       $bytes .= PelConvert::longToBytes($entry->getComponents()$order);
  1084.       
  1085.       /*
  1086.        * Size? If bigger than 4 bytes, the actual data is not in
  1087.        * the entry but somewhere else.
  1088.        */
  1089.       $data $entry->getBytes($order);
  1090.       $s strlen($data);
  1091.       if ($s 4{
  1092.         Pel::debug('Data size %d too big, storing at offset %d instead.',
  1093.                    $s$end);
  1094.         $bytes .= PelConvert::longToBytes($end$order);
  1095.         $extra_bytes .= $data;
  1096.         $end += $s;
  1097.       else {
  1098.         Pel::debug('Data size %d fits.'$s);
  1099.         /* Copy data directly, pad with NULL bytes as necessary to
  1100.          * fill out the four bytes available.*/
  1101.         $bytes .= $data str_repeat(chr(0)$s);
  1102.       }
  1103.     }
  1104.  
  1105.     if ($this->thumb_data != null{
  1106.       Pel::debug('Appending %d bytes of thumbnail data at %d',
  1107.                  $this->thumb_data->getSize()$end);
  1108.       // TODO: make PelEntry a class that can be constructed with
  1109.       // arguments corresponding to the newt four lines.
  1110.       $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH,
  1111.                                          $order);
  1112.       $bytes .= PelConvert::shortToBytes(PelFormat::LONG$order);
  1113.       $bytes .= PelConvert::longToBytes(1$order);
  1114.       $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(),
  1115.                                         $order);
  1116.       
  1117.       $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT,
  1118.                                          $order);
  1119.       $bytes .= PelConvert::shortToBytes(PelFormat::LONG$order);
  1120.       $bytes .= PelConvert::longToBytes(1$order);
  1121.       $bytes .= PelConvert::longToBytes($end$order);
  1122.       
  1123.       $extra_bytes .= $this->thumb_data->getBytes();
  1124.       $end += $this->thumb_data->getSize();
  1125.     }
  1126.  
  1127.     
  1128.     /* Find bytes from sub IFDs. */
  1129.     $sub_bytes '';
  1130.     foreach ($this->sub as $type => $sub{
  1131.       if ($type == PelIfd::EXIF)
  1132.         $tag PelTag::EXIF_IFD_POINTER;
  1133.       elseif ($type == PelIfd::GPS)
  1134.         $tag PelTag::GPS_INFO_IFD_POINTER;
  1135.       elseif ($type == PelIfd::INTEROPERABILITY)
  1136.         $tag PelTag::INTEROPERABILITY_IFD_POINTER;
  1137.  
  1138.       /* Make an aditional entry with the pointer. */
  1139.       $bytes .= PelConvert::shortToBytes($tag$order);
  1140.       /* Next the format, which is always unsigned long. */
  1141.       $bytes .= PelConvert::shortToBytes(PelFormat::LONG$order);
  1142.       /* There is only one component. */
  1143.       $bytes .= PelConvert::longToBytes(1$order);
  1144.  
  1145.       $data $sub->getBytes($end$order);
  1146.       $s strlen($data);
  1147.       $sub_bytes .= $data;
  1148.  
  1149.       $bytes .= PelConvert::longToBytes($end$order);
  1150.       $end += $s;
  1151.     }
  1152.  
  1153.     /* Make link to next IFD, if any*/
  1154.     if ($this->isLastIFD()) {
  1155.       $link 0;
  1156.     else {
  1157.       $link $end;
  1158.     }
  1159.  
  1160.     Pel::debug('Link to next IFD: %d'$link);
  1161.     
  1162.     $bytes .= PelConvert::longtoBytes($link$order);
  1163.  
  1164.     $bytes .= $extra_bytes $sub_bytes;
  1165.  
  1166.     if (!$this->isLastIfd())
  1167.       $bytes .= $this->next->getBytes($end$order);
  1168.  
  1169.     return $bytes;
  1170.   }
  1171.  
  1172.   
  1173.   /**
  1174.    * Turn this directory into text.
  1175.    *
  1176.    * @return string information about the directory, mainly for
  1177.    *  debugging.
  1178.    */
  1179.   function __toString({
  1180.     $str Pel::fmt("Dumping IFD %s with %d entries...\n",
  1181.                     $this->getName()count($this->entries));
  1182.     
  1183.     foreach ($this->entries as $entry)
  1184.       $str .= $entry->__toString();
  1185.  
  1186.     $str .= Pel::fmt("Dumping %d sub IFDs...\n"count($this->sub));
  1187.  
  1188.     foreach ($this->sub as $type => $ifd)
  1189.       $str .= $ifd->__toString();
  1190.  
  1191.     if ($this->next != null)
  1192.       $str .= $this->next->__toString();
  1193.  
  1194.     return $str;
  1195.   }
  1196.  
  1197.  
  1198. }
  1199.  
  1200. ?>

Documentation generated on Thu, 05 May 2011 07:19:16 +0200 by phpDocumentor 1.4.3