===================================================================
RCS file: ./PEL/RCS/PelJpeg.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelJpeg.php
--- ./PEL/PelJpeg.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelJpeg.php	2011/09/04 21:16:14
@@ -0,0 +1,609 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelJpeg.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes representing JPEG data.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelJpegComment.php');
+require_once('PelJpegContent.php');
+require_once('PelDataWindow.php');
+require_once('PelJpegMarker.php');
+require_once('PelException.php');
+require_once('PelExif.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Exception thrown when an invalid marker is found.
+ *
+ * This exception is thrown when PEL expects to find a {@link
+ * PelJpegMarker} and instead finds a byte that isn't a known marker.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelJpegInvalidMarkerException extends PelException {
+
+  /**
+   * Construct a new invalid marker exception.
+   *
+   * The exception will contain a message describing the error,
+   * including the byte found and the offset of the offending byte.
+   *
+   * @param int the byte found.
+   *
+   * @param int the offset in the data.
+   */
+  function __construct($marker, $offset) {
+    parent::__construct('Invalid marker found at offset %d: 0x%2X',
+                        $offset, $marker);
+  }
+}
+
+/**
+ * Class for handling JPEG data.
+ *
+ * The {@link PelJpeg} class defined here provides an abstraction for
+ * dealing with a JPEG file.  The file will be contain a number of
+ * sections containing some {@link PelJpegContent content} identified
+ * by a {@link PelJpegMarker marker}.
+ *
+ * The {@link getExif()} method is used get hold of the {@link
+ * PelJpegMarker::APP1 APP1} section which stores Exif data.  So if
+ * the name of the JPEG file is stored in $filename, then one would
+ * get hold of the Exif data by saying:
+ *
+ * <code>
+ * $jpeg = new PelJpeg($filename);
+ * $exif = $jpeg->getExif();
+ * $tiff = $exif->getTiff();
+ * $ifd0 = $tiff->getIfd();
+ * $exif = $ifd0->getSubIfd(PelIfd::EXIF);
+ * $ifd1 = $ifd0->getNextIfd();
+ * </code>
+ *
+ * The $idf0 and $ifd1 variables will then be two {@link PelTiff TIFF}
+ * {@link PelIfd Image File Directories}, in which the data is stored
+ * under the keys found in {@link PelTag}.
+ *
+ * Should one have some image data (in the form of a {@link
+ * PelDataWindow}) of an unknown type, then the {@link
+ * PelJpeg::isValid()} function is handy: it will quickly test if the
+ * data could be valid JPEG data.  The {@link PelTiff::isValid()}
+ * function does the same for TIFF images.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelJpeg {
+
+  /**
+   * The sections in the JPEG data.
+   *
+   * A JPEG file is built up as a sequence of sections, each section
+   * is identified with a {@link PelJpegMarker}.  Some sections can
+   * occur more than once in the JPEG stream (the {@link
+   * PelJpegMarker::DQT DQT} and {@link PelJpegMarker::DHT DTH}
+   * markers for example) and so this is an array of ({@link
+   * PelJpegMarker}, {@link PelJpegContent}) pairs.
+   *
+   * The content can be either generic {@link PelJpegContent JPEG
+   * content} or {@link PelExif Exif data}.
+   *
+   * @var array
+   */
+  private $sections = array();
+
+  /**
+   * The JPEG image data.
+   *
+   * @var PelDataWindow
+   */
+  private $jpeg_data = null;
+
+  /**
+   * Construct a new JPEG object.
+   *
+   * The new object will be empty unless an argument is given from
+   * which it can initialize itself. This can either be the filename
+   * of a JPEG image, a {@link PelDataWindow} object or a PHP image
+   * resource handle.
+   *
+   * New Exif data (in the form of a {@link PelExif} object) can be
+   * inserted with the {@link setExif()} method:
+   *
+   * <code>
+   * $jpeg = new PelJpeg($data);
+   * // Create container for the Exif information:
+   * $exif = new PelExif();
+   * // Now Add a PelTiff object with a PelIfd object with one or more
+   * // PelEntry objects to $exif... Finally add $exif to $jpeg:
+   * $jpeg->setExif($exif);
+   * </code>
+   *
+   * @param mixed the data that this JPEG. This can either be a
+   * filename, a {@link PelDataWindow} object, or a PHP image resource
+   * handle.
+   */
+  function __construct($data = false) {
+    if ($data === false)
+      return;
+
+    if (is_string($data)) {
+      Pel::debug('Initializing PelJpeg object from %s', $data);
+      $this->loadFile($data);
+    } elseif ($data instanceof PelDataWindow) {
+      Pel::debug('Initializing PelJpeg object from PelDataWindow.');
+      $this->load($data);
+    } elseif (is_resource($data) && get_resource_type($data) == 'gd') {
+      Pel::debug('Initializing PelJpeg object from image resource.');
+      $this->load(new PelDataWindow($data));
+    } else {
+      throw new PelInvalidArgumentException('Bad type for $data: %s', 
+                                            gettype($data));
+    }
+  }
+
+  /**
+   * Load data into a JPEG object.
+   *
+   * The data supplied will be parsed and turned into an object
+   * structure representing the image.  This structure can then be
+   * manipulated and later turned back into an string of bytes.
+   *
+   * This methods can be called at any time after a JPEG object has
+   * been constructed, also after the {@link appendSection()} has been
+   * called to append custom sections.  Loading several JPEG images
+   * into one object will accumulate the sections, but there will only
+   * be one {@link PelJpegMarker::SOS} section at any given time.
+   *
+   * @param PelDataWindow the data that will be turned into JPEG
+   * sections.
+   */
+  function load(PelDataWindow $d) {
+
+    Pel::debug('Parsing %d bytes...', $d->getSize());
+
+    /* JPEG data is stored in big-endian format. */
+    $d->setByteOrder(PelConvert::BIG_ENDIAN);
+    
+    /* Run through the data to read the sections in the image.  After
+     * each section is read, the start of the data window will be
+     * moved forward, and after the last section we'll terminate with
+     * no data left in the window. */
+    while ($d->getSize() > 0) {
+      /* JPEG sections start with 0xFF. The first byte that is not
+       * 0xFF is a marker (hopefully).
+       */
+      for ($i = 0; $i < 7; $i++)
+        if ($d->getByte($i) != 0xFF)
+          break;
+
+      $marker = $d->getByte($i);
+
+      if (!PelJpegMarker::isValid($marker))
+        throw new PelJpegInvalidMarkerException($marker, $i);
+
+      /* Move window so first byte becomes first byte in this
+       * section. */
+      $d->setWindowStart($i+1);
+
+      if ($marker == PelJpegMarker::SOI || $marker == PelJpegMarker::EOI) {
+        $content = new PelJpegContent(new PelDataWindow());
+        $this->appendSection($marker, $content);
+      } else {
+        /* Read the length of the section.  The length includes the
+         * two bytes used to store the length. */
+        $len = $d->getShort(0) - 2;
+        
+        Pel::debug('Found %s section of length %d',
+                   PelJpegMarker::getName($marker), $len);
+
+        /* Skip past the length. */
+        $d->setWindowStart(2);
+
+        if ($marker == PelJpegMarker::APP1) {
+
+          try {
+            $content = new PelExif();
+            $content->load($d->getClone(0, $len));
+          } catch (PelInvalidDataException $e) {
+            /* We store the data as normal JPEG content if it could
+             * not be parsed as Exif data. */
+            $content = new PelJpegContent($d->getClone(0, $len));
+          }
+
+          $this->appendSection($marker, $content);
+          /* Skip past the data. */
+          $d->setWindowStart($len);
+
+        } elseif ($marker == PelJpegMarker::COM) {
+
+          $content = new PelJpegComment();
+          $content->load($d->getClone(0, $len));
+          $this->appendSection($marker, $content);
+          $d->setWindowStart($len);
+
+        } else {
+
+          $content = new PelJpegContent($d->getClone(0, $len));
+          $this->appendSection($marker, $content);
+          /* Skip past the data. */
+          $d->setWindowStart($len);
+          
+          /* In case of SOS, image data will follow. */
+          if ($marker == PelJpegMarker::SOS) {
+            /* Some images have some trailing (garbage?) following the
+             * EOI marker.  To handle this we seek backwards until we
+             * find the EOI marker.  Any trailing content is stored as
+             * a PelJpegContent object. */
+
+            $length = $d->getSize();
+            while ($d->getByte($length-2) != 0xFF ||
+                   $d->getByte($length-1) != PelJpegMarker::EOI) {
+              $length--;
+            }
+
+            $this->jpeg_data = $d->getClone(0, $length-2);
+            Pel::debug('JPEG data: ' . $this->jpeg_data->__toString());
+
+            /* Append the EOI. */
+            $this->appendSection(PelJpegMarker::EOI,
+                                 new PelJpegContent(new PelDataWindow()));
+
+            /* Now check to see if there are any trailing data. */
+            if ($length != $d->getSize()) {
+              Pel::maybeThrow(new PelException('Found trailing content ' .
+                                               'after EOI: %d bytes',
+                                               $d->getSize() - $length));
+              $content = new PelJpegContent($d->getClone($length));
+              /* We don't have a proper JPEG marker for trailing
+               * garbage, so we just use 0x00... */
+              $this->appendSection(0x00, $content);
+            }
+
+            /* Done with the loop. */
+            break;
+          }
+        }
+      }
+    } /* while ($d->getSize() > 0) */
+  }
+
+
+  /**
+   * Load data from a file into a JPEG object.
+   *
+   * @param string the filename.  This must be a readable file.
+   */
+  function loadFile($filename) {
+    $this->load(new PelDataWindow(file_get_contents($filename)));
+  }
+  
+
+  /**
+   * Set Exif data.
+   *
+   * Use this to set the Exif data in the image. This will overwrite
+   * any old Exif information in the image.
+   *
+   * @param PelExif the Exif data.
+   */
+  function setExif(PelExif $exif) {
+    $app0_offset = 1;
+    $app1_offset = -1;
+
+    /* Search through all sections looking for APP0 or APP1. */
+    for ($i = 0; $i < count($this->sections); $i++) {
+      if ($this->sections[$i][0] == PelJpegMarker::APP0) {
+        $app0_offset = $i;
+      } elseif ($this->sections[$i][0] == PelJpegMarker::APP1) {
+        $app1_offset = $i;
+        break;
+      }
+    }
+
+    /* Store the Exif data at the appropriate place, either where the
+     * old Exif data was stored ($app1_offset) or right after APP0
+     * ($app0_offset+1). */
+    if ($app1_offset > 0)
+      $this->sections[$app1_offset][1] = $exif;
+    else
+      $this->insertSection(PelJpegMarker::APP1, $exif, $app0_offset+1);
+  }
+
+
+  /**
+   * Get Exif data.
+   *
+   * Use this to get the @{link PelExif Exif data} stored.
+   *
+   * @return PelExif the Exif data found or null if the image has no
+   * Exif data.
+   */
+  function getExif() {
+    $exif = $this->getSection(PelJpegMarker::APP1);
+    if ($exif instanceof PelExif)
+      return $exif;
+    else
+      return null;
+  }
+
+
+  /**
+   * Clear any Exif data.
+   *
+   * This method will only clear the first @{link PelJpegMarker::APP1}
+   * section found (there should normally be just one).
+   */
+  function clearExif() {
+    for ($i = 0; $i < count($this->sections); $i++) {
+      if ($this->sections[$i][0] == PelJpegMarker::APP1) {
+        unset($this->sections[$i]);
+        return;
+      }
+    }
+  }
+
+
+  /**
+   * Append a new section.
+   *
+   * Used only when loading an image. If it used again later, then the
+   * section will end up after the @{link PelJpegMarker::EOI EOI
+   * marker} and will probably not be useful.
+   *
+   * Please use @{link setExif()} instead if you intend to add Exif
+   * information to an image as that function will know the right
+   * place to insert the data.
+   *
+   * @param PelJpegMarker the marker identifying the new section.
+   *
+   * @param PelJpegContent the content of the new section.
+   */
+  function appendSection($marker, PelJpegContent $content) {
+    $this->sections[] = array($marker, $content);
+  }
+
+
+  /**
+   * Insert a new section.
+   *
+   * Please use @{link setExif()} instead if you intend to add Exif
+   * information to an image as that function will know the right
+   * place to insert the data.
+   *
+   * @param PelJpegMarker the marker for the new section.
+   *
+   * @param PelJpegContent the content of the new section.
+   *
+   * @param int the offset where the new section will be inserted ---
+   * use 0 to insert it at the very beginning, use 1 to insert it
+   * between sections 1 and 2, etc.
+   */
+  function insertSection($marker, PelJpegContent $content, $offset) {
+    array_splice($this->sections, $offset, 0, array(array($marker, $content)));
+  }
+
+
+  /**
+   * Get a section corresponding to a particular marker.
+   *
+   * Please use the {@link getExif()} if you just need the Exif data.
+   *
+   * This will search through the sections of this JPEG object,
+   * looking for a section identified with the specified {@link
+   * PelJpegMarker marker}.  The {@link PelJpegContent content} will
+   * then be returned.  The optional argument can be used to skip over
+   * some of the sections.  So if one is looking for the, say, third
+   * {@link PelJpegMarker::DHT DHT} section one would do:
+   *
+   * <code>
+   * $dht3 = $jpeg->getSection(PelJpegMarker::DHT, 2);
+   * </code>
+   *
+   * @param PelJpegMarker the marker identifying the section.
+   *
+   * @param int the number of sections to be skipped.  This must be a
+   * non-negative integer.
+   *
+   * @return PelJpegContent the content found, or null if there is no
+   * content available.
+   */
+  function getSection($marker, $skip = 0) {
+    foreach ($this->sections as $s) {
+      if ($s[0] == $marker)
+        if ($skip > 0)
+          $skip--;
+        else
+          return $s[1];
+    }
+
+    return null;        
+  }
+
+
+  /**
+   * Get all sections.
+   *
+   * @return array an array of ({@link PelJpegMarker}, {@link
+   * PelJpegContent}) pairs.  Each pair is an array with the {@link
+   * PelJpegMarker} as the first element and the {@link
+   * PelJpegContent} as the second element, so the return type is an
+   * array of arrays.
+   *
+   * So to loop through all the sections in a given JPEG image do
+   * this:
+   *
+   * <code>
+   * foreach ($jpeg->getSections() as $section) {
+   *   $marker = $section[0];
+   *   $content = $section[1];
+   *   // Use $marker and $content here.
+   * }
+   * </code>
+   *
+   * instead of this:
+   *
+   * <code>
+   * foreach ($jpeg->getSections() as $marker => $content) {
+   *   // Does not work the way you would think...
+   * }
+   * </code>
+   *
+   * The problem is that there could be several sections with the same
+   * marker, and thus a simple associative array does not suffice.
+   */
+  function getSections() {
+    return $this->sections;
+  }
+
+
+  /**
+   * Turn this JPEG object into bytes.
+   *
+   * The bytes returned by this method is ready to be stored in a file
+   * as a valid JPEG image. Use the {@link saveFile()} convenience
+   * method to do this.
+   *
+   * @return string bytes representing this JPEG object, including all
+   * its sections and their associated data.
+   */
+  function getBytes() {
+    $bytes = '';
+
+    foreach ($this->sections as $section) {
+      $m = $section[0];
+      $c = $section[1];
+
+      /* Write the marker */
+      $bytes .= "\xFF" . PelJpegMarker::getBytes($m);
+      /* Skip over empty markers. */
+      if ($m == PelJpegMarker::SOI || $m == PelJpegMarker::EOI)
+        continue;
+
+      $data = $c->getBytes();
+      $size = strlen($data) + 2;
+      
+      $bytes .= PelConvert::shortToBytes($size, PelConvert::BIG_ENDIAN);
+      $bytes .= $data;
+      
+      /* In case of SOS, we need to write the JPEG data. */
+      if ($m == PelJpegMarker::SOS)
+        $bytes .= $this->jpeg_data->getBytes();
+    }
+
+    return $bytes;
+
+  }
+
+
+  /**
+   * Save the JPEG object as a JPEG image in a file.
+   *
+   * @param string the filename to save in. An existing file with the
+   * same name will be overwritten!
+   */
+  function saveFile($filename) {
+    file_put_contents($filename, $this->getBytes());
+  }
+
+
+  /**
+   * Make a string representation of this JPEG object.
+   *
+   * This is mainly usefull for debugging.  It will show the structure
+   * of the image, and its sections.
+   *
+   * @return string debugging information about this JPEG object.
+   */
+  function __toString() {
+    $str = Pel::tra("Dumping JPEG data...\n");
+    for ($i = 0; $i < count($this->sections); $i++) {
+      $m = $this->sections[$i][0];
+      $c = $this->sections[$i][1];
+      $str .= Pel::fmt("Section %d (marker 0x%02X - %s):\n",
+                       $i, $m, PelJpegMarker::getName($m));
+      $str .= Pel::fmt("  Description: %s\n",
+                       PelJpegMarker::getDescription($m));
+      
+      if ($m == PelJpegMarker::SOI ||
+          $m == PelJpegMarker::EOI)
+        continue;
+      
+      if ($c instanceof PelExif) {
+        $str .= Pel::tra("  Content    : Exif data\n");
+        $str .= $c->__toString() . "\n";
+      } elseif ($c instanceof PelJpegComment) {
+        $str .= Pel::fmt("  Content    : %s\n", $c->getValue());
+      } else {
+        $str .= Pel::tra("  Content    : Unknown\n");
+      }
+    }
+
+    return $str;
+  }
+
+
+  /**
+   * Test data to see if it could be a valid JPEG image.
+   *
+   * The function will only look at the first few bytes of the data,
+   * and try to determine if it could be a valid JPEG image based on
+   * those bytes.  This means that the check is more like a heuristic
+   * than a rigorous check.
+   *
+   * @param PelDataWindow the bytes that will be checked.
+   *
+   * @return boolean true if the bytes look like the beginning of a
+   * JPEG image, false otherwise.
+   *
+   * @see PelTiff::isValid()
+   */
+  static function isValid(PelDataWindow $d) {
+    /* JPEG data is stored in big-endian format. */
+    $d->setByteOrder(PelConvert::BIG_ENDIAN);
+    
+    for ($i = 0; $i < 7; $i++)
+      if ($d->getByte($i) != 0xFF)
+        break;
+    
+    return $d->getByte($i) == PelJpegMarker::SOI;
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelIfd.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelIfd.php
--- ./PEL/PelIfd.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelIfd.php	2011/09/04 21:16:14
@@ -0,0 +1,1200 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007, 2008  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelIfd.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes for dealing with Exif IFDs.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntryUndefined.php');
+require_once('PelEntryRational.php');
+require_once('PelDataWindow.php');
+require_once('PelEntryAscii.php');
+require_once('PelEntryShort.php');
+require_once('PelEntryByte.php');
+require_once('PelEntryLong.php');
+require_once('PelException.php');
+require_once('PelFormat.php');
+require_once('PelEntry.php');
+require_once('PelTag.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Exception indicating a general problem with the IFD.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelIfdException extends PelException {}
+
+/**
+ * Class representing an Image File Directory (IFD).
+ *
+ * {@link PelTiff TIFF data} is structured as a number of Image File
+ * Directories, IFDs for short.  Each IFD contains a number of {@link
+ * PelEntry entries}, some data and finally a link to the next IFD.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelIfd implements IteratorAggregate, ArrayAccess {
+
+  /**
+   * Main image IFD.
+   *
+   * Pass this to the constructor when creating an IFD which will be
+   * the IFD of the main image.
+   */
+  const IFD0 = 0;
+
+  /**
+   * Thumbnail image IFD.
+   *
+   * Pass this to the constructor when creating an IFD which will be
+   * the IFD of the thumbnail image.
+   */
+  const IFD1 = 1;
+
+  /**
+   * Exif IFD.
+   *
+   * Pass this to the constructor when creating an IFD which will be
+   * the Exif sub-IFD.
+   */
+  const EXIF = 2;
+
+  /**
+   * GPS IFD.
+   *
+   * Pass this to the constructor when creating an IFD which will be
+   * the GPS sub-IFD.
+   */
+  const GPS  = 3;
+
+  /**
+   * Interoperability IFD.
+   *
+   * Pass this to the constructor when creating an IFD which will be
+   * the interoperability sub-IFD.
+   */
+  const INTEROPERABILITY = 4;
+
+  /**
+   * The entries held by this directory.
+   *
+   * Each tag in the directory is represented by a {@link PelEntry}
+   * object in this array.
+   *
+   * @var array
+   */
+  private $entries = array();
+
+  /**
+   * The type of this directory.
+   *
+   * Initialized in the constructor.  Must be one of {@link IFD0},
+   * {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link
+   * INTEROPERABILITY}.
+   *
+   * @var int
+   */
+  private $type;
+
+  /**
+   * The next directory.
+   *
+   * This will be initialized in the constructor, or be left as null
+   * if this is the last directory.
+   *
+   * @var PelIfd
+   */
+  private $next = null;
+
+  /**
+   * Sub-directories pointed to by this directory.
+   *
+   * This will be an array of ({@link PelTag}, {@link PelIfd}) pairs.
+   *
+   * @var array
+   */
+  private $sub = array();
+
+  /**
+   * The thumbnail data.
+   *
+   * This will be initialized in the constructor, or be left as null
+   * if there are no thumbnail as part of this directory.
+   *
+   * @var PelDataWindow
+   */
+  private $thumb_data = null;
+  // TODO: use this format to choose between the
+  // JPEG_INTERCHANGE_FORMAT and STRIP_OFFSETS tags.
+  // private $thumb_format;
+
+  
+  /**
+   * Construct a new Image File Directory (IFD).
+   *
+   * The IFD will be empty, use the {@link addEntry()} method to add
+   * an {@link PelEntry}.  Use the {@link setNext()} method to link
+   * this IFD to another.
+   *
+   * @param int type the type of this IFD.  Must be one of {@link
+   * IFD0}, {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link
+   * INTEROPERABILITY}.  An {@link PelIfdException} will be thrown
+   * otherwise.
+   */
+  function __construct($type) {
+    if ($type != PelIfd::IFD0 && $type != PelIfd::IFD1 &&
+        $type != PelIfd::EXIF && $type != PelIfd::GPS &&
+        $type != PelIfd::INTEROPERABILITY)
+      throw new PelIfdException('Unknown IFD type: %d', $type);
+
+    $this->type = $type;
+  }
+
+
+  /**
+   * Load data into a Image File Directory (IFD).
+   *
+   * @param PelDataWindow the data window that will provide the data.
+   *
+   * @param int the offset within the window where the directory will
+   * be found.
+   */
+  function load(PelDataWindow $d, $offset) {
+    $thumb_offset = 0;
+    $thumb_length = 0;
+
+    Pel::debug('Constructing IFD at offset %d from %d bytes...',
+               $offset, $d->getSize());
+
+    /* Read the number of entries */
+    $n = $d->getShort($offset);
+    Pel::debug('Loading %d entries...', $n);
+    
+    $offset += 2;
+
+    /* Check if we have enough data. */
+    if ($offset + 12 * $n > $d->getSize()) {
+      $n = floor(($offset - $d->getSize()) / 12);
+      Pel::maybeThrow(new PelIfdException('Adjusted to: %d.', $n));
+    }
+
+    for ($i = 0; $i < $n; $i++) {
+      // TODO: increment window start instead of using offsets.
+      $tag = $d->getShort($offset + 12 * $i);
+      Pel::debug('Loading entry with tag 0x%04X: %s (%d of %d)...',
+                 $tag, PelTag::getName($this->type, $tag), $i + 1, $n);
+      
+      switch ($tag) {
+      case PelTag::EXIF_IFD_POINTER:
+      case PelTag::GPS_INFO_IFD_POINTER:
+      case PelTag::INTEROPERABILITY_IFD_POINTER:
+        $o = $d->getLong($offset + 12 * $i + 8);
+        Pel::debug('Found sub IFD at offset %d', $o);
+
+        /* Map tag to IFD type. */
+        if ($tag == PelTag::EXIF_IFD_POINTER)
+          $type = PelIfd::EXIF;
+        elseif ($tag == PelTag::GPS_INFO_IFD_POINTER)
+          $type = PelIfd::GPS;
+        elseif ($tag == PelTag::INTEROPERABILITY_IFD_POINTER)
+          $type = PelIfd::INTEROPERABILITY;
+
+        $this->sub[$type] = new PelIfd($type);
+        $this->sub[$type]->load($d, $o);
+        break;
+      case PelTag::JPEG_INTERCHANGE_FORMAT:
+        $thumb_offset = $d->getLong($offset + 12 * $i + 8);
+        $this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
+        break;
+      case PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH:
+        $thumb_length = $d->getLong($offset + 12 * $i + 8);
+        $this->safeSetThumbnail($d, $thumb_offset, $thumb_length);
+        break;
+      default:
+        $format     = $d->getShort($offset + 12 * $i + 2);
+        $components = $d->getLong($offset + 12 * $i + 4);
+        
+        /* The data size.  If bigger than 4 bytes, the actual data is
+         * not in the entry but somewhere else, with the offset stored
+         * in the entry.
+         */
+        $s = PelFormat::getSize($format) * $components;
+        if ($s > 0) {    
+          $doff = $offset + 12 * $i + 8;
+          if ($s > 4)
+            $doff = $d->getLong($doff);
+
+          $data = $d->getClone($doff, $s);
+        } else {
+          $data = new PelDataWindow();
+        }
+
+        try {
+          $entry = $this->newEntryFromData($tag, $format, $components, $data);
+          $this->addEntry($entry);
+        } catch (PelException $e) {
+          /* Throw the exception when running in strict mode, store
+           * otherwise. */
+          Pel::maybeThrow($e);
+        }
+
+        /* The format of the thumbnail is stored in this tag. */
+//         TODO: handle TIFF thumbnail.
+//         if ($tag == PelTag::COMPRESSION) {
+//           $this->thumb_format = $data->getShort();
+//         }
+        break;
+      }
+    }
+
+    /* Offset to next IFD */
+    $o = $d->getLong($offset + 12 * $n);
+    Pel::debug('Current offset is %d, link at %d points to %d.',
+               $offset,  $offset + 12 * $n, $o);
+
+    if ($o > 0) {
+      /* Sanity check: we need 6 bytes  */
+      if ($o > $d->getSize() - 6) {
+        Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: ' .
+                                            '%d > %d!',
+                                            $o, $d->getSize() - 6));
+      } else {
+        if ($this->type == PelIfd::IFD1) // IFD1 shouldn't link further...
+          Pel::maybeThrow(new PelIfdException('IFD1 links to another IFD!'));
+
+        $this->next = new PelIfd(PelIfd::IFD1);
+        $this->next->load($d, $o);
+      }
+    } else {
+      Pel::debug('Last IFD.');
+    }
+  }
+
+
+  /**
+   * Make a new entry from a bunch of bytes.
+   *
+   * This method will create the proper subclass of {@link PelEntry}
+   * corresponding to the {@link PelTag} and {@link PelFormat} given.
+   * The entry will be initialized with the data given.
+   *
+   * Please note that the data you pass to this method should come
+   * from an image, that is, it should be raw bytes.  If instead you
+   * want to create an entry for holding, say, an short integer, then
+   * create a {@link PelEntryShort} object directly and load the data
+   * into it.
+   *
+   * A {@link PelUnexpectedFormatException} is thrown if a mismatch is
+   * discovered between the tag and format, and likewise a {@link
+   * PelWrongComponentCountException} is thrown if the number of
+   * components does not match the requirements of the tag.  The
+   * requirements for a given tag (if any) can be found in the
+   * documentation for {@link PelTag}.
+   *
+   * @param PelTag the tag of the entry.
+   *
+   * @param PelFormat the format of the entry.
+   *
+   * @param int the components in the entry.
+   *
+   * @param PelDataWindow the data which will be used to construct the
+   * entry.
+   *
+   * @return PelEntry a newly created entry, holding the data given.
+   */
+  function newEntryFromData($tag, $format, $components, PelDataWindow $data) {
+
+    /* First handle tags for which we have a specific PelEntryXXX
+     * class. */
+
+    switch ($this->type) {
+
+    case self::IFD0:
+    case self::IFD1:
+    case self::EXIF:
+    case self::INTEROPERABILITY:
+
+      switch ($tag) {
+      case PelTag::DATE_TIME:
+      case PelTag::DATE_TIME_ORIGINAL:
+      case PelTag::DATE_TIME_DIGITIZED:
+        if ($format != PelFormat::ASCII)
+          throw new PelUnexpectedFormatException($this->type, $tag, $format,
+                                               PelFormat::ASCII);
+
+        if ($components != 20)
+          throw new PelWrongComponentCountException($this->type, $tag, $components, 20);
+
+        // TODO: handle timezones.
+        return new PelEntryTime($tag, $data->getBytes(0, -1), PelEntryTime::EXIF_STRING);
+
+      case PelTag::COPYRIGHT:
+        if ($format != PelFormat::ASCII)
+          throw new PelUnexpectedFormatException($this->type, $tag, $format,
+                                                 PelFormat::ASCII);
+
+        $v = explode("\0", trim($data->getBytes(), ' '));
+        return new PelEntryCopyright($v[0], $v[1]);
+
+      case PelTag::EXIF_VERSION:
+      case PelTag::FLASH_PIX_VERSION:
+      case PelTag::INTEROPERABILITY_VERSION:
+        if ($format != PelFormat::UNDEFINED)
+          throw new PelUnexpectedFormatException($this->type, $tag, $format,
+                                               PelFormat::UNDEFINED);
+
+        return new PelEntryVersion($tag, $data->getBytes() / 100);
+
+      case PelTag::USER_COMMENT:
+        if ($format != PelFormat::UNDEFINED)
+          throw new PelUnexpectedFormatException($this->type, $tag, $format,
+                                                 PelFormat::UNDEFINED);
+        if ($data->getSize() < 8) {
+          return new PelEntryUserComment();
+        } else {
+          return new PelEntryUserComment($data->getBytes(8),
+                                       rtrim($data->getBytes(0, 8)));
+        }
+
+      case PelTag::XP_TITLE:
+      case PelTag::XP_COMMENT:
+      case PelTag::XP_AUTHOR:
+      case PelTag::XP_KEYWORDS:
+      case PelTag::XP_SUBJECT:
+        if ($format != PelFormat::BYTE)
+          throw new PelUnexpectedFormatException($this->type, $tag, $format,
+                                               PelFormat::BYTE);
+
+        $v = '';
+        for ($i = 0; $i < $components; $i++) {
+          $b = $data->getByte($i);
+          /* Convert the byte to a character if it is non-null ---
+           * information about the character encoding of these entries
+           * would be very nice to have!  So far my tests have shown
+           * that characters in the Latin-1 character set are stored in
+           * a single byte followed by a NULL byte. */
+          if ($b != 0)
+            $v .= chr($b);
+        }
+
+        return new PelEntryWindowsString($tag, $v);
+      }
+
+    case self::GPS:
+      
+    default:
+      /* Then handle the basic formats. */
+      switch ($format) {
+      case PelFormat::BYTE:
+        $v =  new PelEntryByte($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getByte($i));
+        return $v;
+
+      case PelFormat::SBYTE:
+        $v =  new PelEntrySByte($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getSByte($i));
+        return $v;
+
+      case PelFormat::ASCII:
+        return new PelEntryAscii($tag, $data->getBytes(0, -1));
+
+      case PelFormat::SHORT:
+        $v =  new PelEntryShort($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getShort($i*2));
+        return $v;
+
+      case PelFormat::SSHORT:
+        $v =  new PelEntrySShort($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getSShort($i*2));
+        return $v;
+
+      case PelFormat::LONG:
+        $v =  new PelEntryLong($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getLong($i*4));
+        return $v;
+
+      case PelFormat::SLONG:
+        $v =  new PelEntrySLong($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getSLong($i*4));
+        return $v;
+
+      case PelFormat::RATIONAL:
+        $v =  new PelEntryRational($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getRational($i*8));
+        return $v;
+
+      case PelFormat::SRATIONAL:
+        $v =  new PelEntrySRational($tag);
+        for ($i = 0; $i < $components; $i++)
+          $v->addNumber($data->getSRational($i*8));
+        return $v;
+
+      case PelFormat::UNDEFINED:
+        return new PelEntryUndefined($tag, $data->getBytes());
+
+      default:
+        throw new PelException('Unsupported format: %s',
+                               PelFormat::getName($format));
+      }
+    }
+  }
+
+
+
+
+  /**
+   * Extract thumbnail data safely.
+   *
+   * It is safe to call this method repeatedly with either the offset
+   * or the length set to zero, since it requires both of these
+   * arguments to be positive before the thumbnail is extracted.
+   *
+   * When both parameters are set it will check the length against the
+   * available data and adjust as necessary. Only then is the
+   * thumbnail data loaded.
+   *
+   * @param PelDataWindow the data from which the thumbnail will be
+   * extracted.
+   *
+   * @param int the offset into the data.
+   *
+   * @param int the length of the thumbnail.
+   */
+  private function safeSetThumbnail(PelDataWindow $d, $offset, $length) {
+    /* Load the thumbnail if both the offset and the length is
+     * available. */
+    if ($offset > 0 && $length > 0) {
+      /* Some images have a broken length, so we try to carefully
+       * check the length before we store the thumbnail. */
+      if ($offset + $length > $d->getSize()) {
+        Pel::maybeThrow(new PelIfdException('Thumbnail length %d bytes ' .
+                                            'adjusted to %d bytes.',
+                                            $length,
+                                            $d->getSize() - $offset));
+        $length = $d->getSize() - $offset;
+      }
+
+      /* Now set the thumbnail normally. */
+      $this->setThumbnail($d->getClone($offset, $length));
+    }
+  }
+
+  
+  /**
+   * Set thumbnail data.
+   *
+   * Use this to embed an arbitrary JPEG image within this IFD. The
+   * data will be checked to ensure that it has a proper {@link
+   * PelJpegMarker::EOI} at the end.  If not, then the length is
+   * adjusted until one if found.  An {@link PelIfdException} might be
+   * thrown (depending on {@link Pel::$strict}) this case.
+   *
+   * @param PelDataWindow the thumbnail data.
+   */
+  function setThumbnail(PelDataWindow $d) {
+    $size = $d->getSize();
+    /* Now move backwards until we find the EOI JPEG marker. */
+    while ($d->getByte($size - 2) != 0xFF ||
+           $d->getByte($size - 1) != PelJpegMarker::EOI) {
+      $size--;
+    }
+
+    if ($size != $d->getSize())
+      Pel::maybeThrow(new PelIfdException('Decrementing thumbnail size ' .
+                                          'to %d bytes', $size));
+    
+    $this->thumb_data = $d->getClone(0, $size);
+  }
+
+
+  /**
+   * Get the type of this directory.
+   *
+   * @return int of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, {@link
+   * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
+   * PelIfd::INTEROPERABILITY}.
+   */
+  function getType() {
+    return $this->type;
+  }
+
+
+  /**
+   * Is a given tag valid for this IFD?
+   *
+   * Different types of IFDs can contain different kinds of tags ---
+   * the {@link IFD0} type, for example, cannot contain a {@link
+   * PelTag::GPS_LONGITUDE} tag.
+   *
+   * A special exception is tags with values above 0xF000.  They are
+   * treated as private tags and will be allowed everywhere (use this
+   * for testing or for implementing your own types of tags).
+   *
+   * @param PelTag the tag.
+   *
+   * @return boolean true if the tag is considered valid in this IFD,
+   * false otherwise.
+   *
+   * @see getValidTags()
+   */
+  function isValidTag($tag) {
+    return $tag > 0xF000 || in_array($tag, $this->getValidTags());
+  }
+
+
+  /**
+   * Returns a list of valid tags for this IFD.
+   *
+   * @return array an array of {@link PelTag}s which are valid for
+   * this IFD.
+   */
+  function getValidTags() {
+    switch ($this->type) {
+    case PelIfd::IFD0:
+    case PelIfd::IFD1:
+      return array(PelTag::IMAGE_WIDTH,
+                   PelTag::IMAGE_LENGTH,
+                   PelTag::BITS_PER_SAMPLE,
+                   PelTag::COMPRESSION,
+                   PelTag::PHOTOMETRIC_INTERPRETATION,
+                   PelTag::IMAGE_DESCRIPTION,
+                   PelTag::MAKE,
+                   PelTag::MODEL,
+                   PelTag::STRIP_OFFSETS,
+                   PelTag::ORIENTATION,
+                   PelTag::SAMPLES_PER_PIXEL,
+                   PelTag::ROWS_PER_STRIP,
+                   PelTag::STRIP_BYTE_COUNTS,
+                   PelTag::X_RESOLUTION,
+                   PelTag::Y_RESOLUTION,
+                   PelTag::PLANAR_CONFIGURATION,
+                   PelTag::RESOLUTION_UNIT,
+                   PelTag::TRANSFER_FUNCTION,
+                   PelTag::SOFTWARE,
+                   PelTag::DATE_TIME,
+                   PelTag::ARTIST,
+                   PelTag::WHITE_POINT,
+                   PelTag::PRIMARY_CHROMATICITIES,
+                   PelTag::JPEG_INTERCHANGE_FORMAT,
+                   PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH,
+                   PelTag::YCBCR_COEFFICIENTS,
+                   PelTag::YCBCR_SUB_SAMPLING,
+                   PelTag::YCBCR_POSITIONING,
+                   PelTag::REFERENCE_BLACK_WHITE,
+                   PelTag::COPYRIGHT,
+                   PelTag::EXIF_IFD_POINTER,
+                   PelTag::GPS_INFO_IFD_POINTER,
+                   PelTag::PRINT_IM,
+                   PelTag::XP_TITLE,
+                   PelTag::XP_COMMENT,
+                   PelTag::XP_AUTHOR,
+                   PelTag::XP_KEYWORDS,
+                   PelTag::XP_SUBJECT);
+
+    case PelIfd::EXIF:
+      return array(PelTag::EXPOSURE_TIME,
+                   PelTag::FNUMBER,
+                   PelTag::EXPOSURE_PROGRAM,
+                   PelTag::SPECTRAL_SENSITIVITY,
+                   PelTag::ISO_SPEED_RATINGS,
+                   PelTag::OECF,
+                   PelTag::EXIF_VERSION,
+                   PelTag::DATE_TIME_ORIGINAL,
+                   PelTag::DATE_TIME_DIGITIZED,
+                   PelTag::COMPONENTS_CONFIGURATION,
+                   PelTag::COMPRESSED_BITS_PER_PIXEL,
+                   PelTag::SHUTTER_SPEED_VALUE,
+                   PelTag::APERTURE_VALUE,
+                   PelTag::BRIGHTNESS_VALUE,
+                   PelTag::EXPOSURE_BIAS_VALUE,
+                   PelTag::MAX_APERTURE_VALUE,
+                   PelTag::SUBJECT_DISTANCE,
+                   PelTag::METERING_MODE,
+                   PelTag::LIGHT_SOURCE,
+                   PelTag::FLASH,
+                   PelTag::FOCAL_LENGTH,
+                   PelTag::MAKER_NOTE,
+                   PelTag::USER_COMMENT,
+                   PelTag::SUB_SEC_TIME,
+                   PelTag::SUB_SEC_TIME_ORIGINAL,
+                   PelTag::SUB_SEC_TIME_DIGITIZED,
+                   PelTag::FLASH_PIX_VERSION,
+                   PelTag::COLOR_SPACE,
+                   PelTag::PIXEL_X_DIMENSION,
+                   PelTag::PIXEL_Y_DIMENSION,
+                   PelTag::RELATED_SOUND_FILE,
+                   PelTag::FLASH_ENERGY,
+                   PelTag::SPATIAL_FREQUENCY_RESPONSE,
+                   PelTag::FOCAL_PLANE_X_RESOLUTION,
+                   PelTag::FOCAL_PLANE_Y_RESOLUTION,
+                   PelTag::FOCAL_PLANE_RESOLUTION_UNIT,
+                   PelTag::SUBJECT_LOCATION,
+                   PelTag::EXPOSURE_INDEX,
+                   PelTag::SENSING_METHOD,
+                   PelTag::FILE_SOURCE,
+                   PelTag::SCENE_TYPE,
+                   PelTag::CFA_PATTERN,
+                   PelTag::CUSTOM_RENDERED,
+                   PelTag::EXPOSURE_MODE,
+                   PelTag::WHITE_BALANCE,
+                   PelTag::DIGITAL_ZOOM_RATIO,
+                   PelTag::FOCAL_LENGTH_IN_35MM_FILM,
+                   PelTag::SCENE_CAPTURE_TYPE,
+                   PelTag::GAIN_CONTROL,
+                   PelTag::CONTRAST,
+                   PelTag::SATURATION,
+                   PelTag::SHARPNESS,
+                   PelTag::DEVICE_SETTING_DESCRIPTION,
+                   PelTag::SUBJECT_DISTANCE_RANGE,
+                   PelTag::IMAGE_UNIQUE_ID,
+                   PelTag::INTEROPERABILITY_IFD_POINTER,
+                   PelTag::GAMMA);
+
+    case PelIfd::GPS:
+      return array(PelTag::GPS_VERSION_ID, 
+                   PelTag::GPS_LATITUDE_REF, 
+                   PelTag::GPS_LATITUDE, 
+                   PelTag::GPS_LONGITUDE_REF, 
+                   PelTag::GPS_LONGITUDE, 
+                   PelTag::GPS_ALTITUDE_REF,
+                   PelTag::GPS_ALTITUDE,
+                   PelTag::GPS_TIME_STAMP,
+                   PelTag::GPS_SATELLITES,
+                   PelTag::GPS_STATUS,
+                   PelTag::GPS_MEASURE_MODE,
+                   PelTag::GPS_DOP,
+                   PelTag::GPS_SPEED_REF,
+                   PelTag::GPS_SPEED,
+                   PelTag::GPS_TRACK_REF,
+                   PelTag::GPS_TRACK,
+                   PelTag::GPS_IMG_DIRECTION_REF,
+                   PelTag::GPS_IMG_DIRECTION,
+                   PelTag::GPS_MAP_DATUM,
+                   PelTag::GPS_DEST_LATITUDE_REF,
+                   PelTag::GPS_DEST_LATITUDE,
+                   PelTag::GPS_DEST_LONGITUDE_REF,
+                   PelTag::GPS_DEST_LONGITUDE,
+                   PelTag::GPS_DEST_BEARING_REF,
+                   PelTag::GPS_DEST_BEARING,
+                   PelTag::GPS_DEST_DISTANCE_REF,
+                   PelTag::GPS_DEST_DISTANCE,
+                   PelTag::GPS_PROCESSING_METHOD,
+                   PelTag::GPS_AREA_INFORMATION,
+                   PelTag::GPS_DATE_STAMP,
+                   PelTag::GPS_DIFFERENTIAL);
+
+    case PelIfd::INTEROPERABILITY:
+      return array(PelTag::INTEROPERABILITY_INDEX, 
+                   PelTag::INTEROPERABILITY_VERSION,
+                   PelTag::RELATED_IMAGE_FILE_FORMAT, 
+                   PelTag::RELATED_IMAGE_WIDTH, 
+                   PelTag::RELATED_IMAGE_LENGTH);
+
+      /* TODO: Where do these tags belong?
+PelTag::FILL_ORDER,
+PelTag::DOCUMENT_NAME, 
+PelTag::TRANSFER_RANGE, 
+PelTag::JPEG_PROC, 
+PelTag::BATTERY_LEVEL, 
+PelTag::IPTC_NAA, 
+PelTag::INTER_COLOR_PROFILE, 
+PelTag::CFA_REPEAT_PATTERN_DIM, 
+      */
+    }
+  }
+
+
+  /**
+   * Get the name of an IFD type.
+   *
+   * @param int one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1},
+   * {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
+   * PelIfd::INTEROPERABILITY}.
+   *
+   * @return string the name of type.
+   */
+  static function getTypeName($type) {
+    switch ($type) {
+    case self::IFD0:
+      return '0';
+    case self::IFD1:
+      return '1';
+    case self::EXIF:
+      return 'Exif';
+    case self::GPS:
+      return 'GPS';
+    case self::INTEROPERABILITY:
+      return 'Interoperability';
+    default:
+      throw new PelIfdException('Unknown IFD type: %d', $type);
+    }
+  }
+
+
+  /**
+   * Get the name of this directory.
+   *
+   * @return string the name of this directory.
+   */
+  function getName() {
+    return $this->getTypeName($this->type);
+  }
+
+
+  /**
+   * Adds an entry to the directory.
+   *
+   * @param PelEntry the entry that will be added. If the entry is not
+   * valid in this IFD (as per {@link isValidTag()}) an
+   * {@link PelInvalidDataException} is thrown.
+   *
+   * @todo The entry will be identified with its tag, so each
+   * directory can only contain one entry with each tag.  Is this a
+   * bug?
+   */
+  function addEntry(PelEntry $e) {
+    if ($this->isValidTag($e->getTag())) {
+      $e->setIfdType($this->type);
+      $this->entries[$e->getTag()] = $e;
+    } else {
+      throw new PelInvalidDataException("IFD %s cannot hold\n%s",
+                                        $this->getName(), $e->__toString());
+    }
+  }
+
+
+  /**
+   * Does a given tag exist in this IFD?
+   *
+   * This methods is part of the ArrayAccess SPL interface for
+   * overriding array access of objects, it allows you to check for
+   * existance of an entry in the IFD:
+   *
+   * <code>
+   * if (isset($ifd[PelTag::FNUMBER]))
+   *   // ... do something with the F-number.
+   * </code>
+   *
+   * @param PelTag the offset to check.
+   *
+   * @return boolean whether the tag exists.
+   */
+  function offsetExists($tag) {
+    return isset($this->entries[$tag]);
+  }
+
+
+  /**
+   * Retrieve a given tag from this IFD.
+   *
+   * This methods is part of the ArrayAccess SPL interface for
+   * overriding array access of objects, it allows you to read entries
+   * from the IFD the same was as for an array:
+   *
+   * <code>
+   * $entry = $ifd[PelTag::FNUMBER];
+   * </code>
+   *
+   * @param PelTag the tag to return.  It is an error to ask for a tag
+   * which is not in the IFD, just like asking for a non-existant
+   * array entry.
+   *
+   * @return PelEntry the entry.
+   */
+  function offsetGet($tag) {
+    return $this->entries[$tag];
+  }
+
+
+  /**
+   * Set or update a given tag in this IFD.
+   *
+   * This methods is part of the ArrayAccess SPL interface for
+   * overriding array access of objects, it allows you to add new
+   * entries or replace esisting entries by doing:
+   *
+   * <code>
+   * $ifd[PelTag::EXPOSURE_BIAS_VALUE] = $entry;
+   * </code>
+   *
+   * Note that the actual array index passed is ignored!  Instead the
+   * {@link PelTag} from the entry is used.
+   *
+   * @param PelTag the offset to update.
+   *
+   * @param PelEntry the new value.
+   */
+  function offsetSet($tag, $e) {
+    if ($e instanceof PelEntry) {
+      $tag = $e->getTag();
+      $this->entries[$tag] = $e;
+    } else {
+      throw new PelInvalidArgumentException('Argument "%s" must be a PelEntry.', $e);
+    }
+  }
+
+
+  /**
+   * Unset a given tag in this IFD.
+   *
+   * This methods is part of the ArrayAccess SPL interface for
+   * overriding array access of objects, it allows you to delete
+   * entries in the IFD by doing:
+   *
+   * <code>
+   * unset($ifd[PelTag::EXPOSURE_BIAS_VALUE])
+   * </code>
+   *
+   * @param PelTag the offset to delete.
+   */
+  function offsetUnset($tag) {
+    unset($this->entries[$tag]);
+  }
+
+
+  /**
+   * Retrieve an entry.
+   *
+   * @param PelTag the tag identifying the entry.
+   *
+   * @return PelEntry the entry associated with the tag, or null if no
+   * such entry exists.
+   */
+  function getEntry($tag) {
+    if (isset($this->entries[$tag]))
+      return $this->entries[$tag];
+    else
+      return null;
+  }
+
+
+  /**
+   * Returns all entries contained in this IFD.
+   *
+   * @return array an array of {@link PelEntry} objects, or rather
+   * descendant classes.  The array has {@link PelTag}s as keys
+   * and the entries as values.
+   *
+   * @see getEntry
+   * @see getIterator
+   */
+  function getEntries() {
+    return $this->entries;
+  }
+
+  
+  /**
+   * Return an iterator for all entries contained in this IFD.
+   *
+   * Used with foreach as in
+   *
+   * <code>
+   * foreach ($ifd as $tag => $entry) {
+   *   // $tag is now a PelTag and $entry is a PelEntry object.
+   * }
+   * </code>
+   *
+   * @return Iterator an iterator using the {@link PelTag tags} as
+   * keys and the entries as values.
+   */
+  function getIterator() {
+    return new ArrayIterator($this->entries);
+  }
+  
+
+  /**
+   * Returns available thumbnail data.
+   *
+   * @return string the bytes in the thumbnail, if any.  If the IFD
+   * does not contain any thumbnail data, the empty string is
+   * returned.
+   *
+   * @todo Throw an exception instead when no data is available?
+   *
+   * @todo Return the $this->thumb_data object instead of the bytes?
+   */
+  function getThumbnailData() {
+    if ($this->thumb_data != null)
+      return $this->thumb_data->getBytes();
+    else
+      return '';
+  }
+  
+
+  /**
+   * Make this directory point to a new directory.
+   *
+   * @param PelIfd the IFD that this directory will point to.
+   */
+  function setNextIfd(PelIfd $i) {
+    $this->next = $i;
+  }
+
+
+  /**
+   * Return the IFD pointed to by this directory.
+   *
+   * @return PelIfd the next IFD, following this IFD. If this is the
+   * last IFD, null is returned.
+   */
+  function getNextIfd() {
+    return $this->next;
+  }
+
+
+  /**
+   * Check if this is the last IFD.
+   *
+   * @return boolean true if there are no following IFD, false
+   * otherwise.
+   */
+  function isLastIfd() {
+    return $this->next == null;
+  }
+
+
+  /**
+   * Add a sub-IFD.
+   *
+   * Any previous sub-IFD of the same type will be overwritten.
+   *
+   * @param PelIfd the sub IFD.  The type of must be one of {@link
+   * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
+   * PelIfd::INTEROPERABILITY}.
+   */
+  function addSubIfd(PelIfd $sub) {
+    $this->sub[$sub->type] = $sub;
+  }
+
+
+  /**
+   * Return a sub IFD.
+   *
+   * @param int the type of the sub IFD.  This must be one of {@link
+   * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
+   * PelIfd::INTEROPERABILITY}.
+   *
+   * @return PelIfd the IFD associated with the type, or null if that
+   * sub IFD does not exist.
+   */
+  function getSubIfd($type) {
+    if (isset($this->sub[$type]))
+      return $this->sub[$type];
+    else
+      return null;
+  }
+
+
+  /**
+   * Get all sub IFDs.
+   *
+   * @return array an associative array with (IFD-type, {@link
+   * PelIfd}) pairs.
+   */
+  function getSubIfds() {
+    return $this->sub;
+  }
+
+
+  /**
+   * Turn this directory into bytes.
+   *
+   * This directory will be turned into a byte string, with the
+   * specified byte order.  The offsets will be calculated from the
+   * offset given.
+   *
+   * @param int the offset of the first byte of this directory.
+   *
+   * @param PelByteOrder the byte order that should be used when
+   * turning integers into bytes.  This should be one of {@link
+   * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
+   */
+  function getBytes($offset, $order) {
+    $bytes = '';
+    $extra_bytes = '';
+
+    Pel::debug('Bytes from IDF will start at offset %d within Exif data',
+               $offset);
+    
+    $n = count($this->entries) + count($this->sub);
+    if ($this->thumb_data != null) {
+      /* We need two extra entries for the thumbnail offset and
+       * length. */
+      $n += 2;
+    }
+
+    $bytes .= PelConvert::shortToBytes($n, $order);
+
+    /* Initialize offset of extra data.  This included the bytes
+     * preceding this IFD, the bytes needed for the count of entries,
+     * the entries themselves (and sub entries), the extra data in the
+     * entries, and the IFD link.
+     */
+    $end = $offset + 2 + 12 * $n + 4;
+
+    foreach ($this->entries as $tag => $entry) {
+      /* Each entry is 12 bytes long. */
+      $bytes .= PelConvert::shortToBytes($entry->getTag(), $order);
+      $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order);
+      $bytes .= PelConvert::longToBytes($entry->getComponents(), $order);
+      
+      /*
+       * Size? If bigger than 4 bytes, the actual data is not in
+       * the entry but somewhere else.
+       */
+      $data = $entry->getBytes($order);
+      $s = strlen($data);
+      if ($s > 4) {
+        Pel::debug('Data size %d too big, storing at offset %d instead.',
+                   $s, $end);
+        $bytes .= PelConvert::longToBytes($end, $order);
+        $extra_bytes .= $data;
+        $end += $s;
+      } else {
+        Pel::debug('Data size %d fits.', $s);
+        /* Copy data directly, pad with NULL bytes as necessary to
+         * fill out the four bytes available.*/
+        $bytes .= $data . str_repeat(chr(0), 4 - $s);
+      }
+    }
+
+    if ($this->thumb_data != null) {
+      Pel::debug('Appending %d bytes of thumbnail data at %d',
+                 $this->thumb_data->getSize(), $end);
+      // TODO: make PelEntry a class that can be constructed with
+      // arguments corresponding to the newt four lines.
+      $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH,
+                                         $order);
+      $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
+      $bytes .= PelConvert::longToBytes(1, $order);
+      $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(),
+                                        $order);
+      
+      $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT,
+                                         $order);
+      $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
+      $bytes .= PelConvert::longToBytes(1, $order);
+      $bytes .= PelConvert::longToBytes($end, $order);
+      
+      $extra_bytes .= $this->thumb_data->getBytes();
+      $end += $this->thumb_data->getSize();
+    }
+
+    
+    /* Find bytes from sub IFDs. */
+    $sub_bytes = '';
+    foreach ($this->sub as $type => $sub) {
+      if ($type == PelIfd::EXIF)
+        $tag = PelTag::EXIF_IFD_POINTER;
+      elseif ($type == PelIfd::GPS)
+        $tag = PelTag::GPS_INFO_IFD_POINTER;
+      elseif ($type == PelIfd::INTEROPERABILITY)
+        $tag = PelTag::INTEROPERABILITY_IFD_POINTER;
+
+      /* Make an aditional entry with the pointer. */
+      $bytes .= PelConvert::shortToBytes($tag, $order);
+      /* Next the format, which is always unsigned long. */
+      $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order);
+      /* There is only one component. */
+      $bytes .= PelConvert::longToBytes(1, $order);
+
+      $data = $sub->getBytes($end, $order);
+      $s = strlen($data);
+      $sub_bytes .= $data;
+
+      $bytes .= PelConvert::longToBytes($end, $order);
+      $end += $s;
+    }
+
+    /* Make link to next IFD, if any*/
+    if ($this->isLastIFD()) {
+      $link = 0;
+    } else {
+      $link = $end;
+    }
+
+    Pel::debug('Link to next IFD: %d', $link);
+    
+    $bytes .= PelConvert::longtoBytes($link, $order);
+
+    $bytes .= $extra_bytes . $sub_bytes;
+
+    if (!$this->isLastIfd())
+      $bytes .= $this->next->getBytes($end, $order);
+
+    return $bytes;
+  }
+
+  
+  /**
+   * Turn this directory into text.
+   *
+   * @return string information about the directory, mainly for
+   * debugging.
+   */
+  function __toString() {
+    $str = Pel::fmt("Dumping IFD %s with %d entries...\n",
+                    $this->getName(), count($this->entries));
+    
+    foreach ($this->entries as $entry)
+      $str .= $entry->__toString();
+
+    $str .= Pel::fmt("Dumping %d sub IFDs...\n", count($this->sub));
+
+    foreach ($this->sub as $type => $ifd)
+      $str .= $ifd->__toString();
+
+    if ($this->next != null)
+      $str .= $this->next->__toString();
+
+    return $str;
+  }
+
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelTag.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelTag.php
--- ./PEL/PelTag.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelTag.php	2011/09/04 21:16:14
@@ -0,0 +1,1974 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelTag.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Namespace for functions operating on Exif tags.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('Pel.php');
+require_once('PelIfd.php');
+/**#@-*/
+
+
+/**
+ * Class with static methods for Exif tags.
+ *
+ * This class defines the constants that represents the Exif tags
+ * known to PEL.  They are supposed to be used whenever one needs to
+ * specify an Exif tag, and they will be denoted by the pseudo-type
+ * {@link PelTag} throughout the documentation.
+ *
+ * Please note that the constrains on the format and number of
+ * components given here are advisory only.  To follow the Exif
+ * specification one should obey them, but there is nothing that
+ * prevents you from creating an {@link IMAGE_LENGTH} entry with two
+ * or more components, even though the standard says that there should
+ * be exactly one component.
+ *
+ * All the methods in this class are static and should be called with
+ * the Exif tag on which they should operate.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelTag {
+
+  /**
+   * Interoperability index.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 4.
+   */
+  const INTEROPERABILITY_INDEX                            = 0x0001;
+
+  /**
+   * Interoperability version.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: 4.
+   */
+  const INTEROPERABILITY_VERSION                          = 0x0002;
+
+  /**
+   * Image width.
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const IMAGE_WIDTH                                       = 0x0100;
+
+  /**
+   * Image length.
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const IMAGE_LENGTH                                      = 0x0101;
+
+  /**
+   * Number of bits per component.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 3.
+   */
+  const BITS_PER_SAMPLE                                   = 0x0102;
+
+  /**
+   * Compression scheme.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const COMPRESSION                                       = 0x0103;
+
+  /**
+   * Pixel composition.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const PHOTOMETRIC_INTERPRETATION                        = 0x0106;
+
+  /**
+   * Fill Orde
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const FILL_ORDER                                        = 0x010A;
+
+  /**
+   * Document Name
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const DOCUMENT_NAME                                     = 0x010D;
+
+  /**
+   * Image Description
+   *
+   * Format: {@link PelEntryAscii}.
+   *
+   * Components: any number.
+   */
+  const IMAGE_DESCRIPTION                                 = 0x010E;
+
+  /**
+   * Manufacturer
+   *
+   * Format: {@link PelEntryAscii}.
+   *
+   * Components: any number.
+   */
+  const MAKE                                              = 0x010F;
+
+  /**
+   * Model
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const MODEL                                             = 0x0110;
+
+  /**
+   * Strip Offsets
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: any number.
+   */
+  const STRIP_OFFSETS                                     = 0x0111;
+
+  /**
+   * Orientation of image.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const ORIENTATION                                       = 0x0112;
+
+  /**
+   * Number of components.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SAMPLES_PER_PIXEL                                 = 0x0115;
+
+  /**
+   * Rows per Strip
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const ROWS_PER_STRIP                                    = 0x0116;
+
+  /**
+   * Strip Byte Count
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: any number.
+   */
+  const STRIP_BYTE_COUNTS                                 = 0x0117;
+
+  /**
+   * Image resolution in width direction.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const X_RESOLUTION                                      = 0x011A;
+
+  /**
+   * Image resolution in height direction.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const Y_RESOLUTION                                      = 0x011B;
+
+  /**
+   * Image data arrangement.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const PLANAR_CONFIGURATION                              = 0x011C;
+
+  /**
+   * Unit of X and Y resolution.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const RESOLUTION_UNIT                                   = 0x0128;
+
+  /**
+   * Transfer function.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 3.
+   */
+  const TRANSFER_FUNCTION                                 = 0x012D;
+
+  /**
+   * Software used.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const SOFTWARE                                          = 0x0131;
+
+  /**
+   * File change date and time.
+   *
+   * Format: {@link PelFormat::ASCII}, modelled by the {@link
+   * PelEntryTime} class.
+   *
+   * Components: 20.
+   */
+  const DATE_TIME                                         = 0x0132;
+
+  /**
+   * Person who created the image.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const ARTIST                                            = 0x013B;
+
+  /**
+   * White point chromaticity.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 2.
+   */
+  const WHITE_POINT                                       = 0x013E;
+
+  /**
+   * Chromaticities of primaries.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 6.
+   */
+  const PRIMARY_CHROMATICITIES                            = 0x013F;
+
+  /**
+   * Transfer Range
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const TRANSFER_RANGE                                    = 0x0156;
+
+  /**
+   * JPEGProc
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const JPEG_PROC                                         = 0x0200;
+
+  /**
+   * Offset to JPEG SOI.
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const JPEG_INTERCHANGE_FORMAT                           = 0x0201;
+
+  /**
+   * Bytes of JPEG data.
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const JPEG_INTERCHANGE_FORMAT_LENGTH                    = 0x0202;
+
+  /**
+   * Color space transformation matrix coefficients.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const YCBCR_COEFFICIENTS                                = 0x0211;
+
+  /**
+   * Subsampling ratio of Y to C.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 2.
+   */
+  const YCBCR_SUB_SAMPLING                                = 0x0212;
+
+  /**
+   * Y and C positioning.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const YCBCR_POSITIONING                                 = 0x0213;
+
+  /**
+   * Pair of black and white reference values.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 6.
+   */
+  const REFERENCE_BLACK_WHITE                             = 0x0214;
+
+  /**
+   * Related Image File Format
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const RELATED_IMAGE_FILE_FORMAT                         = 0x1000;
+
+  /**
+   * Related Image Width
+   *
+   * Format: Unknown, probably {@link PelFormat::SHORT}?
+   *
+   * Components: Unknown, probably 1.
+   */
+  const RELATED_IMAGE_WIDTH                               = 0x1001;
+
+  /** Related Image Length
+   *
+   * Format: Unknown, probably {@link PelFormat::SHORT}?
+   *
+   * Components: Unknown, probably 1.
+   */
+  const RELATED_IMAGE_LENGTH                              = 0x1002;
+
+  /**
+   * CFA Repeat Pattern Dim.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 2.
+   */
+  const CFA_REPEAT_PATTERN_DIM                            = 0x828D;
+
+  /**
+   * Battery level.
+   *
+   * Format: Unknown.
+   *
+   * Components: Unknown.
+   */
+  const BATTERY_LEVEL                                     = 0x828F;
+
+  /**
+   * Copyright holder.
+   *
+   * Format: {@link PelFormat::ASCII}, modelled by the {@link
+   * PelEntryCopyright} class.
+   *
+   * Components: any number.
+   */
+  const COPYRIGHT                                         = 0x8298;
+
+  /**
+   * Exposure Time
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const EXPOSURE_TIME                                     = 0x829A;
+
+  /**
+   * FNumber
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FNUMBER                                           = 0x829D;
+
+  /**
+   * IPTC/NAA
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: any number.
+   */
+  const IPTC_NAA                                          = 0x83BB;
+
+  /**
+   * Exif IFD Pointer
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const EXIF_IFD_POINTER                                  = 0x8769;
+
+  /**
+   * Inter Color Profile
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: any number.
+   */
+  const INTER_COLOR_PROFILE                               = 0x8773;
+
+  /**
+   * Exposure Program
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const EXPOSURE_PROGRAM                                  = 0x8822;
+
+  /**
+   * Spectral Sensitivity
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const SPECTRAL_SENSITIVITY                              = 0x8824;
+
+  /**
+   * GPS Info IFD Pointer
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const GPS_INFO_IFD_POINTER                              = 0x8825;
+
+  /**
+   * ISO Speed Ratings
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 2.
+   */
+  const ISO_SPEED_RATINGS                                 = 0x8827;
+
+  /**
+   * OECF
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: any number.
+   */
+  const OECF                                              = 0x8828;
+
+  /**
+   * Exif version.
+   *
+   * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link
+   * PelEntryVersion} class.
+   *
+   * Components: 4.
+   */
+  const EXIF_VERSION                                      = 0x9000;
+
+  /**
+   * Date and time of original data generation.
+   *
+   * Format: {@link PelFormat::ASCII}, modelled by the {@link
+   * PelEntryTime} class.
+   *
+   * Components: 20.
+   */
+  const DATE_TIME_ORIGINAL                                = 0x9003;
+
+  /**
+   * Date and time of digital data generation.
+   *
+   * Format: {@link PelFormat::ASCII}, modelled by the {@link
+   * PelEntryTime} class.
+   *
+   * Components: 20.
+   */
+  const DATE_TIME_DIGITIZED                               = 0x9004;
+
+  /**
+   * Meaning of each component.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: 4.
+   */
+  const COMPONENTS_CONFIGURATION                          = 0x9101;
+
+  /**
+   * Image compression mode.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const COMPRESSED_BITS_PER_PIXEL                         = 0x9102;
+
+  /**
+   * Shutter speed
+   *
+   * Format: {@link PelFormat::SRATIONAL}.
+   *
+   * Components: 1.
+   */
+  const SHUTTER_SPEED_VALUE                               = 0x9201;
+
+  /**
+   * Aperture
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const APERTURE_VALUE                                    = 0x9202;
+
+  /**
+   * Brightness
+   *
+   * Format: {@link PelFormat::SRATIONAL}.
+   *
+   * Components: 1.
+   */
+  const BRIGHTNESS_VALUE                                  = 0x9203;
+
+  /**
+   * Exposure Bias
+   *
+   * Format: {@link PelFormat::SRATIONAL}.
+   *
+   * Components: 1.
+   */
+  const EXPOSURE_BIAS_VALUE                               = 0x9204;
+
+  /**
+   * Max Aperture Value
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const MAX_APERTURE_VALUE                                = 0x9205;
+
+  /**
+   * Subject Distance
+   *
+   * Format: {@link PelFormat::SRATIONAL}.
+   *
+   * Components: 1.
+   */
+  const SUBJECT_DISTANCE                                  = 0x9206;
+
+  /**
+   * Metering Mode
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const METERING_MODE                                     = 0x9207;
+
+  /**
+   * Light Source
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const LIGHT_SOURCE                                      = 0x9208;
+
+  /**
+   * Flash
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const FLASH                                             = 0x9209;
+
+  /**
+   * Focal Length
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FOCAL_LENGTH                                      = 0x920A;
+
+  /**
+   * Subject Area
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 4.
+   */
+  const SUBJECT_AREA                                      = 0x9214;
+
+  /**
+   * Maker Note
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: any number.
+   */
+  const MAKER_NOTE                                        = 0x927C;
+
+  /**
+   * User Comment
+   *
+   * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link
+   * PelEntryUserComment} class.
+   *
+   * Components: any number.
+   */
+  const USER_COMMENT                                      = 0x9286;
+
+  /**
+   * SubSec Time
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const SUB_SEC_TIME                                      = 0x9290;
+
+  /**
+   * SubSec Time Original
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const SUB_SEC_TIME_ORIGINAL                             = 0x9291;
+
+  /**
+   * SubSec Time Digitized
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const SUB_SEC_TIME_DIGITIZED                            = 0x9292;
+
+  /**
+   * Windows XP Title
+   *
+   * Format: {@link PelFormat::BYTE}, modelled by the
+   * {@link PelEntryWindowsString} class.
+   *
+   * Components: any number.
+   */
+  const XP_TITLE                                          = 0x9C9B;
+
+
+  /**
+   * Windows XP Comment
+   *
+   * Format: {@link PelFormat::BYTE}, modelled by the
+   * {@link PelEntryWindowsString} class.
+   *
+   * Components: any number.
+   */
+  const XP_COMMENT                                        = 0x9C9C;
+
+
+  /**
+   * Windows XP Author
+   *
+   * Format: {@link PelFormat::BYTE}, modelled by the
+   * {@link PelEntryWindowsString} class.
+   *
+   * Components: any number.
+   */
+  const XP_AUTHOR                                         = 0x9C9D;
+
+
+  /**
+   * Windows XP Keywords
+   *
+   * Format: {@link PelFormat::BYTE}, modelled by the
+   * {@link PelEntryWindowsString} class.
+   *
+   * Components: any number.
+   */
+  const XP_KEYWORDS                                       = 0x9C9E;
+
+
+  /**
+   * Windows XP Subject
+   *
+   * Format: {@link PelFormat::BYTE}, modelled by the
+   * {@link PelEntryWindowsString} class.
+   *
+   * Components: any number.
+   */
+  const XP_SUBJECT                                        = 0x9C9F;
+
+
+  /**
+   * Supported Flashpix version
+   *
+   * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link
+   * PelEntryVersion} class.
+   *
+   * Components: 4.
+   */
+  const FLASH_PIX_VERSION                                 = 0xA000;
+
+  /**
+   * Color space information.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const COLOR_SPACE                                       = 0xA001;
+
+  /**
+   * Valid image width.
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const PIXEL_X_DIMENSION                                 = 0xA002;
+
+  /**
+   * Valid image height.
+   *
+   * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const PIXEL_Y_DIMENSION                                 = 0xA003;
+
+  /**
+   * Related audio file.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: any number.
+   */
+  const RELATED_SOUND_FILE                                = 0xA004;
+
+  /**
+   * Interoperability IFD Pointer
+   *
+   * Format: {@link PelFormat::LONG}.
+   *
+   * Components: 1.
+   */
+  const INTEROPERABILITY_IFD_POINTER                      = 0xA005;
+
+  /**
+   * Flash energy.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FLASH_ENERGY                                      = 0xA20B;
+
+  /**
+   * Spatial frequency response.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: any number.
+   */
+  const SPATIAL_FREQUENCY_RESPONSE                        = 0xA20C;
+
+  /**
+   * Focal plane X resolution.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FOCAL_PLANE_X_RESOLUTION                          = 0xA20E;
+
+  /**
+   * Focal plane Y resolution.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FOCAL_PLANE_Y_RESOLUTION                          = 0xA20F;
+
+  /**
+   * Focal plane resolution unit.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const FOCAL_PLANE_RESOLUTION_UNIT                       = 0xA210;
+
+  /**
+   * Subject location.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SUBJECT_LOCATION                                  = 0xA214;
+
+  /**
+   * Exposure index.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const EXPOSURE_INDEX                                    = 0xA215;
+
+  /**
+   * Sensing method.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SENSING_METHOD                                    = 0xA217;
+
+  /**
+   * File source.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: 1.
+   */
+  const FILE_SOURCE                                       = 0xA300;
+
+  /**
+   * Scene type.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: 1.
+   */
+  const SCENE_TYPE                                        = 0xA301;
+
+  /**
+   * CFA pattern.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: any number.
+   */
+  const CFA_PATTERN                                       = 0xA302;
+
+  /**
+   * Custom image processing.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const CUSTOM_RENDERED                                   = 0xA401;
+
+  /**
+   * Exposure mode.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const EXPOSURE_MODE                                     = 0xA402;
+
+  /**
+   * White balance.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const WHITE_BALANCE                                     = 0xA403;
+
+  /**
+   * Digital zoom ratio.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const DIGITAL_ZOOM_RATIO                                = 0xA404;
+
+  /**
+   * Focal length in 35mm film.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const FOCAL_LENGTH_IN_35MM_FILM      = 0xA405;
+
+  /**
+   * Scene capture type.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SCENE_CAPTURE_TYPE                                = 0xA406;
+
+  /**
+   * Gain control.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const GAIN_CONTROL                                      = 0xA407;
+
+  /**
+   * Contrast.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const CONTRAST                                          = 0xA408;
+
+  /**
+   * Saturation.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SATURATION                                        = 0xA409;
+
+  /**
+   * Sharpness.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SHARPNESS                                         = 0xA40A;
+
+  /**
+   * Device settings description.
+   *
+   * This tag indicates information on the picture-taking conditions
+   * of a particular camera model.  The tag is used only to indicate
+   * the picture-taking conditions in the reader.
+   */
+  const DEVICE_SETTING_DESCRIPTION                        = 0xA40B;
+
+  /**
+   * Subject distance range.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const SUBJECT_DISTANCE_RANGE                            = 0xA40C;
+
+  /**
+   * Image unique ID.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 32.
+   */
+  const IMAGE_UNIQUE_ID                                   = 0xA420;
+
+  /**
+   * Gamma.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GAMMA                                             = 0xA500;
+
+  /**
+   * PrintIM
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: unknown.
+   */
+  const PRINT_IM                                          = 0xC4A5;
+
+  /**
+   * GPS tag version.
+   *
+   * Format: {@link PelFormat::BYTE}.
+   *
+   * Components: 4.
+   */
+  const GPS_VERSION_ID                                    = 0x0000;
+
+  /**
+   * North or South Latitude.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_LATITUDE_REF                                  = 0x0001;
+
+  /**
+   * Latitude.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const GPS_LATITUDE                                      = 0x0002;
+
+  /**
+   * East or West Longitude.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_LONGITUDE_REF                                 = 0x0003;
+
+  /**
+   * Longitude.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const GPS_LONGITUDE                                     = 0x0004;
+
+  /**
+   * Altitude reference.
+   *
+   * Format: {@link PelFormat::BYTE}.
+   *
+   * Components: 1.
+   */
+  const GPS_ALTITUDE_REF                                  = 0x0005;
+
+  /**
+   * Altitude.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_ALTITUDE                                      = 0x0006;
+
+  /**
+   * GPS time (atomic clock).
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const GPS_TIME_STAMP                                    = 0x0007;
+
+  /**
+   * GPS satellites used for measurement.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: Any.
+   */
+  const GPS_SATELLITES                                    = 0x0008;
+
+  /**
+   * GPS receiver status.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_STATUS                                        = 0x0009;
+
+  /**
+   * GPS measurement mode.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_MEASURE_MODE                                  = 0x000A;
+
+  /**
+   * Measurement precision.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_DOP                                           = 0x000B;
+
+  /**
+   * Speed unit.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_SPEED_REF                                     = 0x000C;
+
+  /**
+   * Speed of GPS receiver.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_SPEED                                         = 0x000D;
+
+  /**
+   * Reference for direction of movement.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_TRACK_REF                                     = 0x000E;
+
+  /**
+   * Direction of movement.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_TRACK                                         = 0x000F;
+
+  /**
+   * Reference for direction of image.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_IMG_DIRECTION_REF                             = 0x0010;
+
+  /**
+   * Direction of image.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_IMG_DIRECTION                                 = 0x0011;
+
+  /**
+   * Geodetic survey data used.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: Any.
+   */
+  const GPS_MAP_DATUM                                     = 0x0012;
+
+  /**
+   * Reference for latitude of destination.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_DEST_LATITUDE_REF                             = 0x0013;
+
+  /**
+   * Latitude of destination.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const GPS_DEST_LATITUDE                                 = 0x0014;
+
+  /**
+   * Reference for longitude of destination.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_DEST_LONGITUDE_REF                            = 0x0015;
+
+  /**
+   * Longitude of destination.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 3.
+   */
+  const GPS_DEST_LONGITUDE                                = 0x0016;
+
+  /**
+   * Reference for bearing of destination.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_DEST_BEARING_REF                              = 0x0017;
+
+  /**
+   * Bearing of destination.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_DEST_BEARING                                  = 0x0018;
+
+  /**
+   * Reference for distance to destination.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 2.
+   */
+  const GPS_DEST_DISTANCE_REF                             = 0x0019;
+
+  /**
+   * Distance to destination.
+   *
+   * Format: {@link PelFormat::RATIONAL}.
+   *
+   * Components: 1.
+   */
+  const GPS_DEST_DISTANCE                                 = 0x001A;
+
+  /**
+   * Name of GPS processing method.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: Any.
+   */
+  const GPS_PROCESSING_METHOD                             = 0x001B;
+
+  /**
+   * Name of GPS area.
+   *
+   * Format: {@link PelFormat::UNDEFINED}.
+   *
+   * Components: Any.
+   */
+  const GPS_AREA_INFORMATION                              = 0x001C;
+
+  /**
+   * GPS date.
+   *
+   * Format: {@link PelFormat::ASCII}.
+   *
+   * Components: 11.
+   */
+  const GPS_DATE_STAMP                                    = 0x001D;
+
+  /**
+   * GPS differential correction.
+   *
+   * Format: {@link PelFormat::SHORT}.
+   *
+   * Components: 1.
+   */
+  const GPS_DIFFERENTIAL                                  = 0x001E;
+
+
+  /**
+   * Returns a short name for an Exif tag.
+   *
+   * @param int the IFD type of the tag, one of {@link PelIfd::IFD0},
+   * {@link PelIfd::IFD1}, {@link PelIfd::EXIF}, {@link PelIfd::GPS},
+   * or {@link PelIfd::INTEROPERABILITY}.
+   *
+   * @param PelTag the tag.
+   *
+   * @return string the short name of the tag, e.g., 'ImageWidth' for
+   * the {@link IMAGE_WIDTH} tag.  If the tag is not known, the string
+   * 'Unknown:0xTTTT' will be returned where 'TTTT' is the hexadecimal
+   * representation of the tag.
+   */
+  static function getName($type, $tag) {
+
+    switch ($type) {
+    case PelIfd::IFD0:
+    case PelIfd::IFD1:
+    case PelIfd::EXIF:
+    case PelIfd::INTEROPERABILITY:
+
+      switch ($tag) {
+      case self::INTEROPERABILITY_INDEX:
+        return 'InteroperabilityIndex';
+      case self::INTEROPERABILITY_VERSION:
+        return 'InteroperabilityVersion';
+      case self::IMAGE_WIDTH:
+        return 'ImageWidth';
+      case self::IMAGE_LENGTH:
+        return 'ImageLength';
+      case self::BITS_PER_SAMPLE:
+        return 'BitsPerSample';
+      case self::COMPRESSION:
+        return 'Compression';
+      case self::PHOTOMETRIC_INTERPRETATION:
+        return 'PhotometricInterpretation';
+      case self::FILL_ORDER:
+        return 'FillOrder';
+      case self::DOCUMENT_NAME:
+        return 'DocumentName';
+      case self::IMAGE_DESCRIPTION:
+        return 'ImageDescription';
+      case self::MAKE:
+        return 'Make';
+      case self::MODEL:
+        return 'Model';
+      case self::STRIP_OFFSETS:
+        return 'StripOffsets';
+      case self::ORIENTATION:
+        return 'Orientation';
+      case self::SAMPLES_PER_PIXEL:
+        return 'SamplesPerPixel';
+      case self::ROWS_PER_STRIP:
+        return 'RowsPerStrip';
+      case self::STRIP_BYTE_COUNTS:
+        return 'StripByteCounts';
+      case self::X_RESOLUTION:
+        return 'XResolution';
+      case self::Y_RESOLUTION:
+        return 'YResolution';
+      case self::PLANAR_CONFIGURATION:
+        return 'PlanarConfiguration';
+      case self::RESOLUTION_UNIT:
+        return 'ResolutionUnit';
+      case self::TRANSFER_FUNCTION:
+        return 'TransferFunction';
+      case self::SOFTWARE:
+        return 'Software';
+      case self::DATE_TIME:
+        return 'DateTime';
+      case self::ARTIST:
+        return 'Artist';
+      case self::WHITE_POINT:
+        return 'WhitePoint';
+      case self::PRIMARY_CHROMATICITIES:
+        return 'PrimaryChromaticities';
+      case self::TRANSFER_RANGE:
+        return 'TransferRange';
+      case self::JPEG_PROC:
+        return 'JPEGProc';
+      case self::JPEG_INTERCHANGE_FORMAT:
+        return 'JPEGInterchangeFormat';
+      case self::JPEG_INTERCHANGE_FORMAT_LENGTH:
+        return 'JPEGInterchangeFormatLength';
+      case self::YCBCR_COEFFICIENTS:
+        return 'YCbCrCoefficients';
+      case self::YCBCR_SUB_SAMPLING:
+        return 'YCbCrSubSampling';
+      case self::YCBCR_POSITIONING:
+        return 'YCbCrPositioning';
+      case self::REFERENCE_BLACK_WHITE:
+        return 'ReferenceBlackWhite';
+      case self::RELATED_IMAGE_FILE_FORMAT:
+        return 'RelatedImageFileFormat';
+      case self::RELATED_IMAGE_WIDTH:
+        return 'RelatedImageWidth';
+      case self::RELATED_IMAGE_LENGTH:
+        return 'RelatedImageLength';
+      case self::CFA_REPEAT_PATTERN_DIM:
+        return 'CFARepeatPatternDim';
+      case self::CFA_PATTERN:
+        return 'CFAPattern';
+      case self::BATTERY_LEVEL:
+        return 'BatteryLevel';
+      case self::COPYRIGHT:
+        return 'Copyright';
+      case self::EXPOSURE_TIME:
+        return 'ExposureTime';
+      case self::FNUMBER:
+        return 'FNumber';
+      case self::IPTC_NAA:
+        return 'IPTC/NAA';
+      case self::EXIF_IFD_POINTER:
+        return 'ExifIFDPointer';
+      case self::INTER_COLOR_PROFILE:
+        return 'InterColorProfile';
+      case self::EXPOSURE_PROGRAM:
+        return 'ExposureProgram';
+      case self::SPECTRAL_SENSITIVITY:
+        return 'SpectralSensitivity';
+      case self::GPS_INFO_IFD_POINTER:
+        return 'GPSInfoIFDPointer';
+      case self::ISO_SPEED_RATINGS:
+        return 'ISOSpeedRatings';
+      case self::OECF:
+        return 'OECF';
+      case self::EXIF_VERSION:
+        return 'ExifVersion';
+      case self::DATE_TIME_ORIGINAL:
+        return 'DateTimeOriginal';
+      case self::DATE_TIME_DIGITIZED:
+        return 'DateTimeDigitized';
+      case self::COMPONENTS_CONFIGURATION:
+        return 'ComponentsConfiguration';
+      case self::COMPRESSED_BITS_PER_PIXEL:
+        return 'CompressedBitsPerPixel';
+      case self::SHUTTER_SPEED_VALUE:
+        return 'ShutterSpeedValue';
+      case self::APERTURE_VALUE:
+        return 'ApertureValue';
+      case self::BRIGHTNESS_VALUE:
+        return 'BrightnessValue';
+      case self::EXPOSURE_BIAS_VALUE:
+        return 'ExposureBiasValue';
+      case self::MAX_APERTURE_VALUE:
+        return 'MaxApertureValue';
+      case self::SUBJECT_DISTANCE:
+        return 'SubjectDistance';
+      case self::METERING_MODE:
+        return 'MeteringMode';
+      case self::LIGHT_SOURCE:
+        return 'LightSource';
+      case self::FLASH:
+        return 'Flash';
+      case self::FOCAL_LENGTH:
+        return 'FocalLength';
+      case self::MAKER_NOTE:
+        return 'MakerNote';
+      case self::USER_COMMENT:
+        return 'UserComment';
+      case self::SUB_SEC_TIME:
+        return 'SubSecTime';
+      case self::SUB_SEC_TIME_ORIGINAL:
+        return 'SubSecTimeOriginal';
+      case self::SUB_SEC_TIME_DIGITIZED:
+        return 'SubSecTimeDigitized';
+      case self::XP_TITLE:
+        return 'WindowsXPTitle';
+      case self::XP_COMMENT:
+        return 'WindowsXPComment';
+      case self::XP_AUTHOR:
+        return 'WindowsXPAuthor';
+      case self::XP_KEYWORDS:
+        return 'WindowsXPKeywords';
+      case self::XP_SUBJECT:
+        return 'WindowsXPSubject';
+      case self::FLASH_PIX_VERSION:
+        return 'FlashPixVersion';
+      case self::COLOR_SPACE:
+        return 'ColorSpace';
+      case self::PIXEL_X_DIMENSION:
+        return 'PixelXDimension';
+      case self::PIXEL_Y_DIMENSION:
+        return 'PixelYDimension';
+      case self::RELATED_SOUND_FILE:
+        return 'RelatedSoundFile';
+      case self::INTEROPERABILITY_IFD_POINTER:
+        return 'InteroperabilityIFDPointer';
+      case self::FLASH_ENERGY:
+        return 'FlashEnergy';
+      case self::SPATIAL_FREQUENCY_RESPONSE:
+        return 'SpatialFrequencyResponse';
+      case self::FOCAL_PLANE_X_RESOLUTION:
+        return 'FocalPlaneXResolution';
+      case self::FOCAL_PLANE_Y_RESOLUTION:
+        return 'FocalPlaneYResolution';
+      case self::FOCAL_PLANE_RESOLUTION_UNIT:
+        return 'FocalPlaneResolutionUnit';
+      case self::SUBJECT_LOCATION:
+        return 'SubjectLocation';
+      case self::EXPOSURE_INDEX:
+        return 'ExposureIndex';
+      case self::SENSING_METHOD:
+        return 'SensingMethod';
+      case self::FILE_SOURCE:
+        return 'FileSource';
+      case self::SCENE_TYPE:
+        return 'SceneType';
+      case self::SUBJECT_AREA:
+        return 'SubjectArea';
+      case self::CUSTOM_RENDERED:
+        return 'CustomRendered';
+      case self::EXPOSURE_MODE:
+        return 'ExposureMode';
+      case self::WHITE_BALANCE:
+        return 'WhiteBalance';
+      case self::DIGITAL_ZOOM_RATIO:
+        return 'DigitalZoomRatio';
+      case self::FOCAL_LENGTH_IN_35MM_FILM:
+        return 'FocalLengthIn35mmFilm';
+      case self::SCENE_CAPTURE_TYPE:
+        return 'SceneCaptureType';
+      case self::GAIN_CONTROL:
+        return 'GainControl';
+      case self::CONTRAST:
+        return 'Contrast';
+      case self::SATURATION:
+        return 'Saturation';
+      case self::SHARPNESS:
+        return 'Sharpness';
+      case self::DEVICE_SETTING_DESCRIPTION:
+        return 'DeviceSettingDescription';
+      case self::SUBJECT_DISTANCE_RANGE:
+        return 'SubjectDistanceRange';
+      case self::IMAGE_UNIQUE_ID:
+        return 'ImageUniqueID';
+      case self::GAMMA:
+        return 'Gamma';
+      case self::PRINT_IM:
+        return 'PrintIM';
+      }
+
+    case PelIfd::GPS:
+      switch ($tag) {
+      case self::GPS_VERSION_ID:
+        return 'GPSVersionID';
+      case self::GPS_LATITUDE_REF:
+        return 'GPSLatitudeRef';
+      case self::GPS_LATITUDE:
+        return 'GPSLatitude';
+      case self::GPS_LONGITUDE_REF:
+        return 'GPSLongitudeRef';
+      case self::GPS_LONGITUDE:
+        return 'GPSLongitude';
+      case self::GPS_ALTITUDE_REF:
+        return 'GPSAltitudeRef';
+      case self::GPS_ALTITUDE:
+        return 'GPSAltitude';
+      case self::GPS_TIME_STAMP:
+        return 'GPSTimeStamp';
+      case self::GPS_SATELLITES:
+        return 'GPSSatellites';
+      case self::GPS_STATUS:
+        return 'GPSStatus';
+      case self::GPS_MEASURE_MODE:
+        return 'GPSMeasureMode';
+      case self::GPS_DOP:
+        return 'GPSDOP';
+      case self::GPS_SPEED_REF:
+        return 'GPSSpeedRef';
+      case self::GPS_SPEED:
+        return 'GPSSpeed';
+      case self::GPS_TRACK_REF:
+        return 'GPSTrackRef';
+      case self::GPS_TRACK:
+        return 'GPSTrack';
+      case self::GPS_IMG_DIRECTION_REF:
+        return 'GPSImgDirectionRef';
+      case self::GPS_IMG_DIRECTION:
+        return 'GPSImgDirection';
+      case self::GPS_MAP_DATUM:
+        return 'GPSMapDatum';
+      case self::GPS_DEST_LATITUDE_REF:
+        return 'GPSDestLatitudeRef';
+      case self::GPS_DEST_LATITUDE:
+        return 'GPSDestLatitude';
+      case self::GPS_DEST_LONGITUDE_REF:
+        return 'GPSDestLongitudeRef';
+      case self::GPS_DEST_LONGITUDE:
+        return 'GPSDestLongitude';
+      case self::GPS_DEST_BEARING_REF:
+        return 'GPSDestBearingRef';
+      case self::GPS_DEST_BEARING:
+        return 'GPSDestBearing';
+      case self::GPS_DEST_DISTANCE_REF:
+        return 'GPSDestDistanceRef';
+      case self::GPS_DEST_DISTANCE:
+        return 'GPSDestDistance';
+      case self::GPS_PROCESSING_METHOD:
+        return 'GPSProcessingMethod';
+      case self::GPS_AREA_INFORMATION:
+        return 'GPSAreaInformation';
+      case self::GPS_DATE_STAMP:
+        return 'GPSDateStamp';
+      case self::GPS_DIFFERENTIAL:
+        return 'GPSDifferential';
+      }
+
+    default:
+      return Pel::fmt('Unknown: 0x%04X', $tag);
+    }
+  }
+
+
+  /**
+   * Returns a title for an Exif tag.
+   *
+   * @param int the IFD type of the tag, one of {@link PelIfd::IFD0},
+   * {@link PelIfd::IFD1}, {@link PelIfd::EXIF}, {@link PelIfd::GPS},
+   * or {@link PelIfd::INTEROPERABILITY}.
+   *
+   * @param PelTag the tag.
+   *
+   * @return string the title of the tag, e.g., 'Image Width' for the
+   * {@link IMAGE_WIDTH} tag.  If the tag isn't known, the string
+   * 'Unknown Tag: 0xTT' will be returned where 'TT' is the
+   * hexadecimal representation of the tag.
+   */
+  function getTitle($type, $tag) {
+
+    switch ($type) {
+    case PelIfd::IFD0:
+    case PelIfd::IFD1:
+    case PelIfd::EXIF:
+    case PelIfd::INTEROPERABILITY:
+
+      switch ($tag) {
+      case self::INTEROPERABILITY_INDEX:
+        return Pel::tra('Interoperability Index');
+      case self::INTEROPERABILITY_VERSION:
+        return Pel::tra('Interoperability Version');
+      case self::IMAGE_WIDTH:
+        return Pel::tra('Image Width');
+      case self::IMAGE_LENGTH:
+        return Pel::tra('Image Length');
+      case self::BITS_PER_SAMPLE:
+        return Pel::tra('Bits per Sample');
+      case self::COMPRESSION:
+        return Pel::tra('Compression');
+      case self::PHOTOMETRIC_INTERPRETATION:
+        return Pel::tra('Photometric Interpretation');
+      case self::FILL_ORDER:
+        return Pel::tra('Fill Order');
+      case self::DOCUMENT_NAME:
+        return Pel::tra('Document Name');
+      case self::IMAGE_DESCRIPTION:
+        return Pel::tra('Image Description');
+      case self::MAKE:
+        return Pel::tra('Manufacturer');
+      case self::MODEL:
+        return Pel::tra('Model');
+      case self::STRIP_OFFSETS:
+        return Pel::tra('Strip Offsets');
+      case self::ORIENTATION:
+        return Pel::tra('Orientation');
+      case self::SAMPLES_PER_PIXEL:
+        return Pel::tra('Samples per Pixel');
+      case self::ROWS_PER_STRIP:
+        return Pel::tra('Rows per Strip');
+      case self::STRIP_BYTE_COUNTS:
+        return Pel::tra('Strip Byte Count');
+      case self::X_RESOLUTION:
+        return Pel::tra('x-Resolution');
+      case self::Y_RESOLUTION:
+        return Pel::tra('y-Resolution');
+      case self::PLANAR_CONFIGURATION:
+        return Pel::tra('Planar Configuration');
+      case self::RESOLUTION_UNIT:
+        return Pel::tra('Resolution Unit');
+      case self::TRANSFER_FUNCTION:
+        return Pel::tra('Transfer Function');
+      case self::SOFTWARE:
+        return Pel::tra('Software');
+      case self::DATE_TIME:
+        return Pel::tra('Date and Time');
+      case self::ARTIST:
+        return Pel::tra('Artist');
+      case self::WHITE_POINT:
+        return Pel::tra('White Point');
+      case self::PRIMARY_CHROMATICITIES:
+        return Pel::tra('Primary Chromaticities');
+      case self::TRANSFER_RANGE:
+        return Pel::tra('Transfer Range');
+      case self::JPEG_PROC:
+        return Pel::tra('JPEG Process');
+      case self::JPEG_INTERCHANGE_FORMAT:
+        return Pel::tra('JPEG Interchange Format');
+      case self::JPEG_INTERCHANGE_FORMAT_LENGTH:
+        return Pel::tra('JPEG Interchange Format Length');
+      case self::YCBCR_COEFFICIENTS:
+        return Pel::tra('YCbCr Coefficients');
+      case self::YCBCR_SUB_SAMPLING:
+        return Pel::tra('YCbCr Sub-Sampling');
+      case self::YCBCR_POSITIONING:
+        return Pel::tra('YCbCr Positioning');
+      case self::REFERENCE_BLACK_WHITE:
+        return Pel::tra('Reference Black/White');
+      case self::RELATED_IMAGE_FILE_FORMAT:
+        return Pel::tra('Related Image File Format');
+      case self::RELATED_IMAGE_WIDTH:
+        return Pel::tra('Related Image Width');
+      case self::RELATED_IMAGE_LENGTH:
+        return Pel::tra('Related Image Length');
+      case self::CFA_REPEAT_PATTERN_DIM:
+        return Pel::tra('CFA Repeat Pattern Dim');
+      case self::CFA_PATTERN:
+        return Pel::tra('CFA Pattern');
+      case self::BATTERY_LEVEL:
+        return Pel::tra('Battery Level');
+      case self::COPYRIGHT:
+        return Pel::tra('Copyright');
+      case self::EXPOSURE_TIME:
+        return Pel::tra('Exposure Time');
+      case self::FNUMBER:
+        return Pel::tra('FNumber');
+      case self::IPTC_NAA:
+        return Pel::tra('IPTC/NAA');
+      case self::EXIF_IFD_POINTER:
+        return Pel::tra('Exif IFD Pointer');
+      case self::INTER_COLOR_PROFILE:
+        return Pel::tra('Inter Color Profile');
+      case self::EXPOSURE_PROGRAM:
+        return Pel::tra('Exposure Program');
+      case self::SPECTRAL_SENSITIVITY:
+        return Pel::tra('Spectral Sensitivity');
+      case self::GPS_INFO_IFD_POINTER:
+        return Pel::tra('GPS Info IFD Pointer');
+      case self::ISO_SPEED_RATINGS:
+        return Pel::tra('ISO Speed Ratings');
+      case self::OECF:
+        return Pel::tra('OECF');
+      case self::EXIF_VERSION:
+        return Pel::tra('Exif Version');
+      case self::DATE_TIME_ORIGINAL:
+        return Pel::tra('Date and Time (original)');
+      case self::DATE_TIME_DIGITIZED:
+        return Pel::tra('Date and Time (digitized)');
+      case self::COMPONENTS_CONFIGURATION:
+        return Pel::tra('Components Configuration');
+      case self::COMPRESSED_BITS_PER_PIXEL:
+        return Pel::tra('Compressed Bits per Pixel');
+      case self::SHUTTER_SPEED_VALUE:
+        return Pel::tra('Shutter speed');
+      case self::APERTURE_VALUE:
+        return Pel::tra('Aperture');
+      case self::BRIGHTNESS_VALUE:
+        return Pel::tra('Brightness');
+      case self::EXPOSURE_BIAS_VALUE:
+        return Pel::tra('Exposure Bias');
+      case self::MAX_APERTURE_VALUE:
+        return Pel::tra('Max Aperture Value');
+      case self::SUBJECT_DISTANCE:
+        return Pel::tra('Subject Distance');
+      case self::METERING_MODE:
+        return Pel::tra('Metering Mode');
+      case self::LIGHT_SOURCE:
+        return Pel::tra('Light Source');
+      case self::FLASH:
+        return Pel::tra('Flash');
+      case self::FOCAL_LENGTH:
+        return Pel::tra('Focal Length');
+      case self::MAKER_NOTE:
+        return Pel::tra('Maker Note');
+      case self::USER_COMMENT:
+        return Pel::tra('User Comment');
+      case self::SUB_SEC_TIME:
+        return Pel::tra('SubSec Time');
+      case self::SUB_SEC_TIME_ORIGINAL:
+        return Pel::tra('SubSec Time Original');
+      case self::SUB_SEC_TIME_DIGITIZED:
+        return Pel::tra('SubSec Time Digitized');
+      case self::XP_TITLE:
+        return 'Windows XP Title';
+      case self::XP_COMMENT:
+        return 'Windows XP Comment';
+      case self::XP_AUTHOR:
+        return 'Windows XP Author';
+      case self::XP_KEYWORDS:
+        return 'Windows XP Keywords';
+      case self::XP_SUBJECT:
+        return 'Windows XP Subject';
+      case self::FLASH_PIX_VERSION:
+        return Pel::tra('FlashPix Version');
+      case self::COLOR_SPACE:
+        return Pel::tra('Color Space');
+      case self::PIXEL_X_DIMENSION:
+        return Pel::tra('Pixel x-Dimension');
+      case self::PIXEL_Y_DIMENSION:
+        return Pel::tra('Pixel y-Dimension');
+      case self::RELATED_SOUND_FILE:
+        return Pel::tra('Related Sound File');
+      case self::INTEROPERABILITY_IFD_POINTER:
+        return Pel::tra('Interoperability IFD Pointer');
+      case self::FLASH_ENERGY:
+        return Pel::tra('Flash Energy');
+      case self::SPATIAL_FREQUENCY_RESPONSE:
+        return Pel::tra('Spatial Frequency Response');
+      case self::FOCAL_PLANE_X_RESOLUTION:
+        return Pel::tra('Focal Plane x-Resolution');
+      case self::FOCAL_PLANE_Y_RESOLUTION:
+        return Pel::tra('Focal Plane y-Resolution');
+      case self::FOCAL_PLANE_RESOLUTION_UNIT:
+        return Pel::tra('Focal Plane Resolution Unit');
+      case self::SUBJECT_LOCATION:
+        return Pel::tra('Subject Location');
+      case self::EXPOSURE_INDEX:
+        return Pel::tra('Exposure index');
+      case self::SENSING_METHOD:
+        return Pel::tra('Sensing Method');
+      case self::FILE_SOURCE:
+        return Pel::tra('File Source');
+      case self::SCENE_TYPE:
+        return Pel::tra('Scene Type');
+      case self::SUBJECT_AREA:
+        return Pel::tra('Subject Area');
+      case self::CUSTOM_RENDERED:
+        return Pel::tra('Custom Rendered');
+      case self::EXPOSURE_MODE:
+        return Pel::tra('Exposure Mode');
+      case self::WHITE_BALANCE:
+        return Pel::tra('White Balance');
+      case self::DIGITAL_ZOOM_RATIO:
+        return Pel::tra('Digital Zoom Ratio');
+      case self::FOCAL_LENGTH_IN_35MM_FILM:
+        return Pel::tra('Focal Length In 35mm Film');
+      case self::SCENE_CAPTURE_TYPE:
+        return Pel::tra('Scene Capture Type');
+      case self::GAIN_CONTROL:
+        return Pel::tra('Gain Control');
+      case self::CONTRAST:
+        return Pel::tra('Contrast');
+      case self::SATURATION:
+        return Pel::tra('Saturation');
+      case self::SHARPNESS:
+        return Pel::tra('Sharpness');
+      case self::DEVICE_SETTING_DESCRIPTION:
+        return Pel::tra('Device Setting Description');
+      case self::SUBJECT_DISTANCE_RANGE:
+        return Pel::tra('Subject Distance Range');
+      case self::IMAGE_UNIQUE_ID:
+        return Pel::tra('Image Unique ID');
+      case self::GAMMA:
+        return Pel::tra('Gamma');
+      case self::PRINT_IM:
+        return Pel::tra('Print IM');
+      }
+
+    case PelIfd::GPS:
+      switch ($tag) {
+      case self::GPS_VERSION_ID:
+        return 'GPSVersionID';
+      case self::GPS_LATITUDE_REF:
+        return 'GPSLatitudeRef';
+      case self::GPS_LATITUDE:
+        return 'GPSLatitude';
+      case self::GPS_LONGITUDE_REF:
+        return 'GPSLongitudeRef';
+      case self::GPS_LONGITUDE:
+        return 'GPSLongitude';
+      case self::GPS_ALTITUDE_REF:
+        return 'GPSAltitudeRef';
+      case self::GPS_ALTITUDE:
+        return 'GPSAltitude';
+      case self::GPS_TIME_STAMP:
+        return 'GPSTimeStamp';
+      case self::GPS_SATELLITES:
+        return 'GPSSatellites';
+      case self::GPS_STATUS:
+        return 'GPSStatus';
+      case self::GPS_MEASURE_MODE:
+        return 'GPSMeasureMode';
+      case self::GPS_DOP:
+        return 'GPSDOP';
+      case self::GPS_SPEED_REF:
+        return 'GPSSpeedRef';
+      case self::GPS_SPEED:
+        return 'GPSSpeed';
+      case self::GPS_TRACK_REF:
+        return 'GPSTrackRef';
+      case self::GPS_TRACK:
+        return 'GPSTrack';
+      case self::GPS_IMG_DIRECTION_REF:
+        return 'GPSImgDirectionRef';
+      case self::GPS_IMG_DIRECTION:
+        return 'GPSImgDirection';
+      case self::GPS_MAP_DATUM:
+        return 'GPSMapDatum';
+      case self::GPS_DEST_LATITUDE_REF:
+        return 'GPSDestLatitudeRef';
+      case self::GPS_DEST_LATITUDE:
+        return 'GPSDestLatitude';
+      case self::GPS_DEST_LONGITUDE_REF:
+        return 'GPSDestLongitudeRef';
+      case self::GPS_DEST_LONGITUDE:
+        return 'GPSDestLongitude';
+      case self::GPS_DEST_BEARING_REF:
+        return 'GPSDestBearingRef';
+      case self::GPS_DEST_BEARING:
+        return 'GPSDestBearing';
+      case self::GPS_DEST_DISTANCE_REF:
+        return 'GPSDestDistanceRef';
+      case self::GPS_DEST_DISTANCE:
+        return 'GPSDestDistance';
+      case self::GPS_PROCESSING_METHOD:
+        return 'GPSProcessingMethod';
+      case self::GPS_AREA_INFORMATION:
+        return 'GPSAreaInformation';
+      case self::GPS_DATE_STAMP:
+        return 'GPSDateStamp';
+      case self::GPS_DIFFERENTIAL:
+        return 'GPSDifferential';
+      }
+
+    default:
+      return Pel::fmt('Unknown Tag: 0x%04X', $tag);
+    }
+  }
+
+}
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelExif.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelExif.php
--- ./PEL/PelExif.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelExif.php	2011/09/04 21:16:14
@@ -0,0 +1,175 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelExif.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes for dealing with Exif data.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelJpegContent.php');
+require_once('PelException.php');
+require_once('PelFormat.php');
+require_once('PelEntry.php');
+require_once('PelTiff.php');
+require_once('PelIfd.php');
+require_once('PelTag.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Class representing Exif data.
+ *
+ * Exif data resides as {@link PelJpegContent data} and consists of a
+ * header followed by a number of {@link PelJpegIfd IFDs}.
+ *
+ * The interesting method in this class is {@link getTiff()} which
+ * will return the {@link PelTiff} object which really holds the data
+ * which one normally think of when talking about Exif data.  This is
+ * because Exif data is stored as an extension of the TIFF file
+ * format.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelExif extends PelJpegContent {
+
+  /**
+   * Exif header.
+   *
+   * The Exif data must start with these six bytes to be considered
+   * valid.
+   */
+  const EXIF_HEADER = "Exif\0\0";
+
+  /**
+   * The PelTiff object contained within.
+   *
+   * @var PelTiff
+   */
+  private $tiff = null;
+
+
+  /**
+   * Construct a new Exif object.
+   *
+   * The new object will be empty --- use the {@link load()} method to
+   * load Exif data from a {@link PelDataWindow} object, or use the
+   * {@link setTiff()} to change the {@link PelTiff} object, which is
+   * the true holder of the Exif {@link PelEntry entries}.
+   */
+  function __construct() {
+
+  }
+
+
+  /**
+   * Load and parse Exif data.
+   *
+   * This will populate the object with Exif data, contained as a
+   * {@link PelTiff} object.  This TIFF object can be accessed with
+   * the {@link getTiff()} method.
+   */
+  function load(PelDataWindow $d) {
+    Pel::debug('Parsing %d bytes of Exif data...', $d->getSize());
+
+    /* There must be at least 6 bytes for the Exif header. */
+    if ($d->getSize() < 6)
+      throw new PelInvalidDataException('Expected at least 6 bytes of Exif ' .
+                                        'data, found just %d bytes.',
+                                        $d->getSize());
+    
+    /* Verify the Exif header */
+    if ($d->strcmp(0, self::EXIF_HEADER)) {
+      $d->setWindowStart(strlen(self::EXIF_HEADER));
+    } else {
+      throw new PelInvalidDataException('Exif header not found.');
+    }
+
+    /* The rest of the data is TIFF data. */
+    $this->tiff = new PelTiff();
+    $this->tiff->load($d);
+  }
+
+
+  /**
+   * Change the TIFF information.
+   *
+   * Exif data is really stored as TIFF data, and this method can be
+   * used to change this data from one {@link PelTiff} object to
+   * another.
+   *
+   * @param PelTiff the new TIFF object.
+   */
+  function setTiff(PelTiff $tiff) {
+    $this->tiff = $tiff;
+  }
+
+
+  /**
+   * Get the underlying TIFF object.
+   *
+   * The actual Exif data is stored in a {@link PelTiff} object, and
+   * this method provides access to it.
+   *
+   * @return PelTiff the TIFF object with the Exif data.
+   */
+  function getTiff() {
+    return $this->tiff;
+  }
+
+
+  /**
+   * Produce bytes for the Exif data.
+   *
+   * @return string bytes representing this object.
+   */
+  function getBytes() {
+    return self::EXIF_HEADER . $this->tiff->getBytes();
+  }
+
+  
+  /**
+   * Return a string representation of this object.
+   *
+   * @return string a string describing this object.  This is mostly
+   * useful for debugging.
+   */
+  function __toString() {
+    return Pel::tra("Dumping Exif data...\n") .
+      $this->tiff->__toString();
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryLong.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryLong.php
--- ./PEL/PelEntryLong.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryLong.php	2011/09/04 21:16:14
@@ -0,0 +1,182 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryLong.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to hold longs, both signed and unsigned.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntryNumber.php');
+/**#@-*/
+
+
+/**
+ * Class for holding unsigned longs.
+ *
+ * This class can hold longs, either just a single long or an array of
+ * longs.  The class will be used to manipulate any of the Exif tags
+ * which can have format {@link PelFormat::LONG} like in this
+ * example:
+ * <code>
+ * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH);
+ * $w->setValue($w->getValue() / 2);
+ * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT);
+ * $h->setValue($h->getValue() / 2);
+ * </code>
+ * Here the width and height is updated to 50% of their original
+ * values.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryLong extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold an unsigned long.
+   *
+   * The method accept its arguments in two forms: several integer
+   * arguments or a single array argument.  The {@link getValue}
+   * method will always return an array except for when a single
+   * integer argument is given here, or when an array with just a
+   * single integer is given.
+   *
+   * This means that one can conveniently use objects like this:
+   * <code>
+   * $a = new PelEntryLong(PelTag::EXIF_IMAGE_WIDTH, 123456);
+   * $b = $a->getValue() - 654321;
+   * </code>
+   * where the call to {@link getValue} will return an integer instead
+   * of an array with one integer element, which would then have to be
+   * extracted.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag},
+   * e.g., {@link PelTag::IMAGE_WIDTH}, or any other tag which can
+   * have format {@link PelFormat::LONG}.
+   *
+   * @param int $value... the long(s) that this entry will
+   * represent or an array of longs.  The argument passed must obey
+   * the same rules as the argument to {@link setValue}, namely that
+   * it should be within range of an unsigned long (32 bit), that is
+   * between 0 and 4294967295 (inclusive).  If not, then a {@link
+   * PelExifOverflowException} will be thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = 0;
+    $this->max    = 4294967295;
+    $this->format = PelFormat::LONG;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return PelConvert::longToBytes($number, $order);
+  }
+}
+
+
+/**
+ * Class for holding signed longs.
+ *
+ * This class can hold longs, either just a single long or an array of
+ * longs.  The class will be used to manipulate any of the Exif tags
+ * which can have format {@link PelFormat::SLONG}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntrySLong extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold a signed long.
+   *
+   * The method accept its arguments in two forms: several integer
+   * arguments or a single array argument.  The {@link getValue}
+   * method will always return an array except for when a single
+   * integer argument is given here, or when an array with just a
+   * single integer is given.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag}
+   * which have format {@link PelFormat::SLONG}.
+   *
+   * @param int $value... the long(s) that this entry will represent
+   * or an array of longs.  The argument passed must obey the same
+   * rules as the argument to {@link setValue}, namely that it should
+   * be within range of a signed long (32 bit), that is between
+   * -2147483648 and 2147483647 (inclusive).  If not, then a {@link
+   * PelOverflowException} will be thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = -2147483648;
+    $this->max    = 2147483647;
+    $this->format = PelFormat::SLONG;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return PelConvert::sLongToBytes($number, $order);
+  }
+}
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelDataWindow.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelDataWindow.php
--- ./PEL/PelDataWindow.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelDataWindow.php	2011/09/04 21:16:14
@@ -0,0 +1,540 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelDataWindow.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * A container for bytes with a limited window of accessible bytes.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelException.php');
+require_once('PelConvert.php');
+/**#@-*/
+
+
+/**
+ * An exception thrown when an invalid offset is encountered.
+ *
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelDataWindowOffsetException extends PelException {}
+
+/**
+ * An exception thrown when an invalid window is encountered.
+ *
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelDataWindowWindowException extends PelException {}
+
+/**
+ * The window.
+ *
+ * @package PEL
+ */
+class PelDataWindow {
+
+  /**
+   * The data held by this window.
+   *
+   * The string can contain any kind of data, including binary data.
+   *
+   * @var string
+   */
+  private $data = '';
+
+  /**
+   * The byte order currently in use.
+   *
+   * This will be the byte order used when data is read using the for
+   * example the {@link getShort} function.  It must be one of {@link
+   * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}.
+   *
+   * @var PelByteOrder
+   * @see setByteOrder, getByteOrder
+   */
+  private $order;
+
+  /**
+   * The start of the current window.
+   *
+   * All offsets used for access into the data will count from this
+   * offset, effectively limiting access to a window starting at this
+   * byte.
+   *
+   * @var int
+   * @see setWindowStart
+   */
+  private $start  = 0;
+
+  /**
+   * The size of the current window.
+   *
+   * All offsets used for access into the data will be limited by this
+   * variable.  A valid offset must be strictly less than this
+   * variable.
+   *
+   * @var int
+   * @see setWindowSize
+   */
+  private $size   = 0;
+
+
+  /**
+   * Construct a new data window with the data supplied.
+   *
+   * @param mixed the data that this window will contain. This can
+   * either be given as a string (interpreted litteraly as a sequence
+   * of bytes) or a PHP image resource handle. The data will be copied
+   * into the new data window.
+   *
+   * @param boolean the initial byte order of the window.  This must
+   * be either {@link PelConvert::LITTLE_ENDIAN} or {@link
+   * PelConvert::BIG_ENDIAN}.  This will be used when integers are
+   * read from the data, and it can be changed later with {@link
+   * setByteOrder()}.
+   */
+  function __construct($data = '', $endianess = PelConvert::LITTLE_ENDIAN) {
+    if (is_string($data)) {
+      $this->data = $data;
+    } elseif (is_resource($data) && get_resource_type($data) == 'gd') {
+      /* The ImageJpeg() function insists on printing the bytes
+       * instead of returning them in a more civil way as a string, so
+       * we have to buffer the output... */
+      ob_start();
+      ImageJpeg($data, null, Pel::getJPEGQuality());
+      $this->data = ob_get_clean();
+    } else {
+      throw new PelInvalidArgumentException('Bad type for $data: %s', 
+                                            gettype($data));
+    }
+
+    $this->order = $endianess;
+    $this->size  = strlen($this->data);
+  }
+
+
+  /**
+   * Get the size of the data window.
+   *
+   * @return int the number of bytes covered by the window.  The
+   * allowed offsets go from 0 up to this number minus one.
+   *
+   * @see getBytes()
+   */
+  function getSize() {
+    return $this->size;
+  }
+
+
+  /**
+   * Change the byte order of the data.
+   *
+   * @param PelByteOrder the new byte order.  This must be either
+   * {@link PelConvert::LITTLE_ENDIAN} or {@link
+   * PelConvert::BIG_ENDIAN}.
+   */
+  function setByteOrder($o) {
+    $this->order = $o;
+  }
+
+
+  /**
+   * Get the currently used byte order.
+   *
+   * @return PelByteOrder this will be either {@link
+   * PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}.
+   */
+  function getByteOrder() {
+    return $this->order;
+  }
+
+
+  /* Move the start of the window forward.
+   *
+   * @param int the new start of the window.  All new offsets will be
+   * calculated from this new start offset, and the size of the window
+   * will shrink to keep the end of the window in place.
+   */
+  function setWindowStart($start) {
+    if ($start < 0 || $start > $this->size)
+      throw new PelDataWindowWindowException('Window [%d, %d] does ' .
+                                             'not fit in window [0, %d]',
+                                             $start, $this->size, $this->size);
+
+    $this->start += $start;
+    $this->size  -= $start;
+  }
+
+
+  /**
+   * Adjust the size of the window.
+   *
+   * The size can only be made smaller.
+   *
+   * @param int the desired size of the window.  If the argument is
+   * negative, the window will be shrunk by the argument.
+   */
+  function setWindowSize($size) {
+    if ($size < 0)
+      $size += $this->size;
+
+    if ($size < 0 || $size > $this->size)
+      throw new PelDataWindowWindowException('Window [0, %d] ' .
+                                             'does not fit in window [0, %d]',
+                                             $size, $this->size);
+    $this->size = $size;
+  }
+  
+
+  /**
+   * Make a new data window with the same data as the this window.
+   *
+   * @param mixed if an integer is supplied, then it will be the start
+   * of the window in the clone.  If left unspecified, then the clone
+   * will inherit the start from this object.
+   *
+   * @param mixed if an integer is supplied, then it will be the size
+   * of the window in the clone.  If left unspecified, then the clone
+   * will inherit the size from this object.
+   *
+   * @return PelDataWindow a new window that operates on the same data
+   * as this window, but (optionally) with a smaller window size.
+   */
+  function getClone($start = false, $size = false) {
+    $c = clone $this;
+    
+    if (is_int($start))
+      $c->setWindowStart($start);
+
+    if (is_int($size))
+      $c->setWindowSize($size);
+
+    return $c;
+  }
+
+
+  /**
+   * Validate an offset against the current window.
+   *
+   * @param int the offset to be validated.  If the offset is negative
+   * or if it is greater than or equal to the current window size,
+   * then a {@link PelDataWindowOffsetException} is thrown.
+   *
+   * @return void if the offset is valid nothing is returned, if it is
+   * invalid a new {@link PelDataWindowOffsetException} is thrown.
+   */
+  private function validateOffset($o) {
+    if ($o < 0 || $o >= $this->size)
+      throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]',
+                                             $o, 0, $this->size-1);
+  }
+
+
+  /**
+   * Return some or all bytes visible in the window.
+   *
+   * This method works just like the standard {@link substr()}
+   * function in PHP with the exception that it works within the
+   * window of accessible bytes and does strict range checking.
+   * 
+   * @param int the offset to the first byte returned.  If a negative
+   * number is given, then the counting will be from the end of the
+   * window.  Invalid offsets will result in a {@link
+   * PelDataWindowOffsetException} being thrown.
+   *
+   * @param int the size of the sub-window.  If a negative number is
+   * given, then that many bytes will be omitted from the result.
+   *
+   * @return string a subset of the bytes in the window.  This will
+   * always return no more than {@link getSize()} bytes.
+   */
+  function getBytes($start = false, $size = false) {
+    if (is_int($start)) {
+      if ($start < 0)
+        $start += $this->size;
+      
+      $this->validateOffset($start);
+    } else {
+      $start = 0;
+    }
+    
+    if (is_int($size)) {
+      if ($size <= 0)
+        $size += $this->size - $start;
+      
+      $this->validateOffset($start+$size);
+    } else {
+      $size = $this->size - $start;
+    }
+
+    return substr($this->data, $this->start + $start, $size);
+  }
+
+
+  /**
+   * Return an unsigned byte from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first byte in the current allowed window.  The last
+   * valid offset is equal to {@link getSize()}-1.  Invalid offsets
+   * will result in a {@link PelDataWindowOffsetException} being
+   * thrown.
+   *
+   * @return  int  the unsigned byte found at offset.
+   */
+  function getByte($o = 0) {
+    /* Validate the offset --- this throws an exception if offset is
+     * out of range. */
+    $this->validateOffset($o);
+
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+    
+    /* Return an unsigned byte. */
+    return PelConvert::bytesToByte($this->data, $o);
+  }
+
+
+  /**
+   * Return a signed byte from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first byte in the current allowed window.  The last
+   * valid offset is equal to {@link getSize()}-1.  Invalid offsets
+   * will result in a {@link PelDataWindowOffsetException} being
+   * thrown.
+   *
+   * @return  int  the signed byte found at offset.
+   */
+  function getSByte($o = 0) {
+    /* Validate the offset --- this throws an exception if offset is
+     * out of range. */
+    $this->validateOffset($o);
+
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+    
+    /* Return a signed byte. */
+    return PelConvert::bytesToSByte($this->data, $o);
+  }
+
+
+  /**
+   * Return an unsigned short read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first short available in the current allowed window.
+   * The last valid offset is equal to {@link getSize()}-2.  Invalid
+   * offsets will result in a {@link PelDataWindowOffsetException}
+   * being thrown.
+   *
+   * @return  int  the unsigned short found at offset.
+   */
+  function getShort($o = 0) {
+    /* Validate the offset+1 to see if we can safely get two bytes ---
+     * this throws an exception if offset is out of range. */
+    $this->validateOffset($o);
+    $this->validateOffset($o+1);
+
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+
+    /* Return an unsigned short. */
+    return PelConvert::bytesToShort($this->data, $o, $this->order);
+  }
+
+
+  /**
+   * Return a signed short read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first short available in the current allowed window.
+   * The last valid offset is equal to {@link getSize()}-2.  Invalid
+   * offsets will result in a {@link PelDataWindowOffsetException}
+   * being thrown.
+   *
+   * @return  int  the signed short found at offset.
+   */
+  function getSShort($o = 0) {
+    /* Validate the offset+1 to see if we can safely get two bytes ---
+     * this throws an exception if offset is out of range. */
+    $this->validateOffset($o);
+    $this->validateOffset($o+1);
+
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+
+    /* Return a signed short. */
+    return PelConvert::bytesToSShort($this->data, $o, $this->order);
+  }
+
+
+  /**
+   * Return an unsigned long read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first long available in the current allowed window.
+   * The last valid offset is equal to {@link getSize()}-4.  Invalid
+   * offsets will result in a {@link PelDataWindowOffsetException}
+   * being thrown.
+   *
+   * @return  int  the unsigned long found at offset.
+   */
+  function getLong($o = 0) {
+    /* Validate the offset+3 to see if we can safely get four bytes
+     * --- this throws an exception if offset is out of range. */
+    $this->validateOffset($o);
+    $this->validateOffset($o+3);
+   
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+
+    /* Return an unsigned long. */
+    return PelConvert::bytesToLong($this->data, $o, $this->order);
+  }
+
+
+  /**
+   * Return a signed long read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first long available in the current allowed window.
+   * The last valid offset is equal to {@link getSize()}-4.  Invalid
+   * offsets will result in a {@link PelDataWindowOffsetException}
+   * being thrown.
+   *
+   * @return  int  the signed long found at offset.
+   */
+  function getSLong($o = 0) {
+    /* Validate the offset+3 to see if we can safely get four bytes
+     * --- this throws an exception if offset is out of range. */
+    $this->validateOffset($o);
+    $this->validateOffset($o+3);
+   
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+
+    /* Return a signed long. */
+    return PelConvert::bytesToSLong($this->data, $o, $this->order);
+  }
+
+
+  /**
+   * Return an unsigned rational read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first rational available in the current allowed
+   * window.  The last valid offset is equal to {@link getSize()}-8.
+   * Invalid offsets will result in a {@link
+   * PelDataWindowOffsetException} being thrown.
+   *
+   * @return array the unsigned rational found at offset.  A rational
+   * number is represented as an array of two numbers: the enumerator
+   * and denominator.  Both of these numbers will be unsigned longs.
+   */
+  function getRational($o = 0) {
+    return array($this->getLong($o), $this->getLong($o+4));
+  }
+
+
+  /**
+   * Return a signed rational read from the data.
+   *
+   * @param int the offset into the data.  An offset of zero will
+   * return the first rational available in the current allowed
+   * window.  The last valid offset is equal to {@link getSize()}-8.
+   * Invalid offsets will result in a {@link
+   * PelDataWindowOffsetException} being thrown.
+   *
+   * @return array the signed rational found at offset.  A rational
+   * number is represented as an array of two numbers: the enumerator
+   * and denominator.  Both of these numbers will be signed longs.
+   */
+  function getSRational($o = 0) {
+    return array($this->getSLong($o), $this->getSLong($o+4));
+  }
+
+
+  /**
+   * String comparison on substrings.
+   *
+   * @param int the offset into the data.  An offset of zero will make
+   * the comparison start with the very first byte available in the
+   * window.  The last valid offset is equal to {@link getSize()}
+   * minus the length of the string.  If the string is too long, then
+   * a {@link PelDataWindowOffsetException} will be thrown.
+   *
+   * @param string the string to compare with.
+   *
+   * @return boolean true if the string given matches the data in the
+   * window, at the specified offset, false otherwise.  The comparison
+   * will stop as soon as a mismatch if found.
+   */
+  function strcmp($o, $str) {
+    /* Validate the offset of the final character we might have to
+     * check. */
+    $s = strlen($str);
+    $this->validateOffset($o);
+    $this->validateOffset($o + $s - 1);
+
+    /* Translate the offset into an offset into the data. */
+    $o += $this->start;
+  
+    /* Check each character, return as soon as the answer is known. */
+    for ($i = 0; $i < $s; $i++) {
+      if ($this->data{$o + $i} != $str{$i})
+        return false;
+    }
+
+    /* All characters matches each other, return true. */
+    return true;
+  }
+
+
+  /**
+   * Return a string representation of the data window.
+   *
+   * @return string a description of the window with information about
+   * the number of bytes accessible, the total number of bytes, and
+   * the window start and stop.
+   */
+  function __toString() {
+    return Pel::fmt('DataWindow: %d bytes in [%d, %d] of %d bytes',
+                    $this->size,
+                    $this->start, $this->start + $this->size,
+                    strlen($this->data));
+  }
+
+}
+
+?>
===================================================================
RCS file: ./PEL/RCS/PelEntryByte.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryByte.php
--- ./PEL/PelEntryByte.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryByte.php	2011/09/04 21:16:14
@@ -0,0 +1,280 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryByte.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to hold bytes, both signed and unsigned.  The {@link
+ * PelEntryWindowsString} class is used to manipulate strings in the
+ * format Windows XP needs.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntryNumber.php');
+/**#@-*/
+
+
+/**
+ * Class for holding unsigned bytes.
+ *
+ * This class can hold bytes, either just a single byte or an array of
+ * bytes.  The class will be used to manipulate any of the Exif tags
+ * which has format {@link PelFormat::BYTE}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryByte extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold an unsigned byte.
+   *
+   * The method accept several integer arguments.  The {@link
+   * getValue} method will always return an array except for when a
+   * single integer argument is given here.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag}
+   * which has format {@link PelFormat::BYTE}.
+   *
+   * @param int $value... the byte(s) that this entry will represent.
+   * The argument passed must obey the same rules as the argument to
+   * {@link setValue}, namely that it should be within range of an
+   * unsigned byte, that is between 0 and 255 (inclusive).  If not,
+   * then a {@link PelOverflowException} will be thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = 0;
+    $this->max    = 255;
+    $this->format = PelFormat::BYTE;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return chr($number);
+  }
+
+}
+
+
+/**
+ * Class for holding signed bytes.
+ *
+ * This class can hold bytes, either just a single byte or an array of
+ * bytes.  The class will be used to manipulate any of the Exif tags
+ * which has format {@link PelFormat::BYTE}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntrySByte extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold a signed byte.
+   *
+   * The method accept several integer arguments.  The {@link getValue}
+   * method will always return an array except for when a single
+   * integer argument is given here.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag}
+   * which has format {@link PelFormat::BYTE}.
+   *
+   * @param int $value... the byte(s) that this entry will represent.
+   * The argument passed must obey the same rules as the argument to
+   * {@link setValue}, namely that it should be within range of a
+   * signed byte, that is between -128 and 127 (inclusive).  If not,
+   * then a {@link PelOverflowException} will be thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = -128;
+    $this->max    = 127;
+    $this->format = PelFormat::SBYTE;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return chr($number);
+  }
+
+}
+
+
+/**
+ * Class used to manipulate strings in the format Windows XP uses.
+ *
+ * When examining the file properties of an image in Windows XP one
+ * can fill in title, comment, author, keyword, and subject fields.
+ * Filling those fields and pressing OK will result in the data being
+ * written into the Exif data in the image.
+ *
+ * The data is written in a non-standard format and can thus not be
+ * loaded directly --- this class is needed to translate it into
+ * normal strings.
+ *
+ * It is important that entries from this class are only created with
+ * the {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT}, {@link
+ * PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link
+ * PelTag::XP_SUBJECT} tags.  If another tag is used the data will no
+ * longer be correctly decoded when reloaded with PEL. (The data will
+ * be loaded as an {@link PelEntryByte} entry, which isn't as useful.)
+ *
+ * This class is to be used as in
+ * <code>
+ * $title = $ifd->getEntry(PelTag::XP_TITLE);
+ * print($title->getValue());
+ * $title->setValue('My favorite cat');
+ * </code>
+ * or if no entry is present one can add a new one with
+ * <code>
+ * $title = new PelEntryWindowsString(PelTag::XP_TITLE, 'A cute dog.');
+ * $ifd->addEntry($title);
+ * </code>
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryWindowsString extends PelEntry {
+
+  /**
+   * The string hold by this entry.
+   *
+   * This is the string that was given to the {@link __construct
+   * constructor} or later to {@link setValue}, without any extra NULL
+   * characters or any such nonsense.
+   *
+   * @var string
+   */
+  private $str;
+
+
+  /**
+   * Make a new PelEntry that can hold a Windows XP specific string.
+   *
+   * @param int the tag which this entry represents.  This should be
+   * one of {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT},
+   * {@link PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link
+   * PelTag::XP_SUBJECT} tags.  If another tag is used, then this
+   * entry will be incorrectly reloaded as a {@link PelEntryByte}.
+   *
+   * @param string the string that this entry will represent.  It will
+   * be passed to {@link setValue} and thus has to obey its
+   * requirements.
+   */
+  function __construct($tag, $str = '') {
+    $this->tag    = $tag;
+    $this->format = PelFormat::BYTE;
+    $this->setValue($str);
+  }
+
+
+  /**
+   * Give the entry a new value.
+   *
+   * This will overwrite the previous value.  The value can be
+   * retrieved later with the {@link getValue} method.
+   *
+   * @param string the new value of the entry.  This should be use the
+   * Latin-1 encoding and be given without any extra NULL characters.
+   */
+  function setValue($str) {
+    $l = strlen($str);
+
+    $this->components = 2 * ($l + 1);
+    $this->str        = $str;
+    $this->bytes      = '';
+    for ($i = 0; $i < $l; $i++)
+      $this->bytes .= $str{$i} . chr(0x00);
+
+    $this->bytes .= chr(0x00) . chr(0x00);
+  }
+
+
+  /**
+   * Return the string of the entry.
+   *
+   * @return string the string held, without any extra NULL
+   * characters.  The string will be the same as the one given to
+   * {@link setValue} or to the {@link __construct constructor}.
+   */
+  function getValue() {
+    return $this->str;
+  }
+
+
+  /**
+   * Return the string of the entry.
+   *
+   * This methods returns the same as {@link getValue}.
+   *
+   * @param boolean not used.
+   *
+   * @return string the string held, without any extra NULL
+   * characters.  The string will be the same as the one given to
+   * {@link setValue} or to the {@link __construct constructor}.
+   */
+  function getText($brief = false) {
+    return $this->str;      
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryRational.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryRational.php
--- ./PEL/PelEntryRational.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryRational.php	2011/09/04 21:16:14
@@ -0,0 +1,290 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryRational.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to manipulate rational numbers.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntryLong.php');
+/**#@-*/
+
+
+/**
+ * Class for holding unsigned rational numbers.
+ *
+ * This class can hold rational numbers, consisting of a numerator and
+ * denominator both of which are of type unsigned long.  Each rational
+ * is represented by an array with just two entries: the numerator and
+ * the denominator, in that order.
+ *
+ * The class can hold either just a single rational or an array of
+ * rationals.  The class will be used to manipulate any of the Exif
+ * tags which can have format {@link PelFormat::RATIONAL} like in this
+ * example:
+ *
+ * <code>
+ * $resolution = $ifd->getEntry(PelTag::X_RESOLUTION);
+ * $resolution->setValue(array(1, 300));
+ * </code>
+ *
+ * Here the x-resolution is adjusted to 1/300, which will be 300 DPI,
+ * unless the {@link PelTag::RESOLUTION_UNIT resolution unit} is set
+ * to something different than 2 which means inches.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryRational extends PelEntryLong {
+
+  /**
+   * Make a new entry that can hold an unsigned rational.
+   *
+   * @param PelTag the tag which this entry represents.  This should
+   * be one of the constants defined in {@link PelTag}, e.g., {@link
+   * PelTag::X_RESOLUTION}, or any other tag which can have format
+   * {@link PelFormat::RATIONAL}.
+   *
+   * @param array $value... the rational(s) that this entry will
+   * represent.  The arguments passed must obey the same rules as the
+   * argument to {@link setValue}, namely that each argument should be
+   * an array with two entries, both of which must be within range of
+   * an unsigned long (32 bit), that is between 0 and 4294967295
+   * (inclusive).  If not, then a {@link PelOverflowException} will be
+   * thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag       = $tag;
+    $this->format    = PelFormat::RATIONAL;
+    $this->dimension = 2;
+    $this->min       = 0;
+    $this->max       = 4294967295;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Format a rational number.
+   *
+   * The rational will be returned as a string with a slash '/'
+   * between the numerator and denominator.
+   *
+   * @param array the rational which will be formatted.
+   *
+   * @param boolean not used.
+   *
+   * @return string the rational formatted as a string suitable for
+   * display.
+   */
+  function formatNumber($number, $brief = false) {
+    return $number[0] . '/' . $number[1];
+  }
+
+
+  /**
+   * Get the value of an entry as text.
+   *
+   * The value will be returned in a format suitable for presentation,
+   * e.g., rationals will be returned as 'x/y', ASCII strings will be
+   * returned as themselves etc.
+   *
+   * @param boolean some values can be returned in a long or more
+   * brief form, and this parameter controls that.
+   *
+   * @return string the value as text.
+   */
+  function getText($brief = false) {
+    if (isset($this->value[0]))
+      $v = $this->value[0];
+
+    switch ($this->tag) {
+    case PelTag::FNUMBER:
+      //CC (e->components, 1, v);
+      return Pel::fmt('f/%.01f', $v[0]/$v[1]);
+
+    case PelTag::APERTURE_VALUE:
+      //CC (e->components, 1, v);
+      //if (!v_rat.denominator) return (NULL);
+      return Pel::fmt('f/%.01f', pow(2, $v[0]/$v[1]/2));
+
+    case PelTag::FOCAL_LENGTH:
+      //CC (e->components, 1, v);
+      //if (!v_rat.denominator) return (NULL);
+      return Pel::fmt('%.1f mm', $v[0]/$v[1]);
+
+    case PelTag::SUBJECT_DISTANCE:
+      //CC (e->components, 1, v);
+      //if (!v_rat.denominator) return (NULL);
+      return Pel::fmt('%.1f m', $v[0]/$v[1]);
+
+    case PelTag::EXPOSURE_TIME:
+      //CC (e->components, 1, v);
+      //if (!v_rat.denominator) return (NULL);
+      if ($v[0]/$v[1] < 1)
+        return Pel::fmt('1/%d sec.', $v[1]/$v[0]);
+      else
+        return Pel::fmt('%d sec.', $v[0]/$v[1]);
+
+    case PelTag::GPS_LATITUDE:
+    case PelTag::GPS_LONGITUDE:
+      $degrees = $this->value[0][0]/$this->value[0][1];
+      $minutes = $this->value[1][0]/$this->value[1][1];
+      $seconds = $this->value[2][0]/$this->value[2][1];
+
+      return sprintf('%s° %s\' %s" (%.2f°)',
+                     $degrees, $minutes, $seconds,
+                     $degrees + $minutes/60 + $seconds/3600);
+
+    default:
+      return parent::getText($brief);
+    }
+  }
+}
+
+
+/**
+ * Class for holding signed rational numbers.
+ *
+ * This class can hold rational numbers, consisting of a numerator and
+ * denominator both of which are of type unsigned long.  Each rational
+ * is represented by an array with just two entries: the numerator and
+ * the denominator, in that order.
+ *
+ * The class can hold either just a single rational or an array of
+ * rationals.  The class will be used to manipulate any of the Exif
+ * tags which can have format {@link PelFormat::SRATIONAL}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntrySRational extends PelEntrySLong {
+
+  /**
+   * Make a new entry that can hold a signed rational.
+   *
+   * @param PelTag the tag which this entry represents.  This should
+   * be one of the constants defined in {@link PelTag}, e.g., {@link
+   * PelTag::SHUTTER_SPEED_VALUE}, or any other tag which can have
+   * format {@link PelFormat::SRATIONAL}.
+   *
+   * @param array $value... the rational(s) that this entry will
+   * represent.  The arguments passed must obey the same rules as the
+   * argument to {@link setValue}, namely that each argument should be
+   * an array with two entries, both of which must be within range of
+   * a signed long (32 bit), that is between -2147483648 and
+   * 2147483647 (inclusive).  If not, then a {@link
+   * PelOverflowException} will be thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag       = $tag;
+    $this->format    = PelFormat::SRATIONAL;
+    $this->dimension = 2;
+    $this->min       = -2147483648;
+    $this->max       = 2147483647;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Format a rational number.
+   *
+   * The rational will be returned as a string with a slash '/'
+   * between the numerator and denominator.  Care is taken to display
+   * '-1/2' instead of the ugly but mathematically equivalent '1/-2'.
+   *
+   * @param array the rational which will be formatted.
+   *
+   * @param boolean not used.
+   *
+   * @return string the rational formatted as a string suitable for
+   * display.
+   */
+  function formatNumber($number, $brief = false) {
+    if ($number[1] < 0)
+      /* Turn output like 1/-2 into -1/2. */
+      return (-$number[0]) . '/' . (-$number[1]);
+    else
+      return $number[0] . '/' . $number[1];
+  }
+
+
+  /**
+   * Get the value of an entry as text.
+   *
+   * The value will be returned in a format suitable for presentation,
+   * e.g., rationals will be returned as 'x/y', ASCII strings will be
+   * returned as themselves etc.
+   *
+   * @param boolean some values can be returned in a long or more
+   * brief form, and this parameter controls that.
+   *
+   * @return string the value as text.
+   */
+  function getText($brief = false) {
+    if (isset($this->value[0]))
+      $v = $this->value[0];
+
+    switch ($this->tag) {
+    case PelTag::SHUTTER_SPEED_VALUE:
+      //CC (e->components, 1, v);
+      //if (!v_srat.denominator) return (NULL);
+      return Pel::fmt('%.0f/%.0f sec. (APEX: %d)',
+                      $v[0], $v[1], pow(sqrt(2), $v[0]/$v[1]));
+
+    case PelTag::BRIGHTNESS_VALUE:
+      //CC (e->components, 1, v);
+      //
+      // TODO: figure out the APEX thing, or remove this so that it is
+      // handled by the default clause at the bottom.
+      return sprintf('%d/%d', $v[0], $v[1]);
+      //FIXME: How do I calculate the APEX value?
+
+    case PelTag::EXPOSURE_BIAS_VALUE:
+      //CC (e->components, 1, v);
+      //if (!v_srat.denominator) return (NULL);
+      return sprintf('%s%.01f', $v[0]*$v[1] > 0 ? '+' : '', $v[0]/$v[1]);
+
+    default:
+      return parent::getText($brief);
+    }
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelConvert.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelConvert.php
--- ./PEL/PelConvert.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelConvert.php	2011/09/04 21:16:14
@@ -0,0 +1,397 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelConvert.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Routines for converting back and forth between bytes and integers.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**
+ * Conversion functions to and from bytes and integers.
+ *
+ * The functions found in this class are used to convert bytes into
+ * integers of several sizes ({@link bytesToShort}, {@link
+ * bytesToLong}, and {@link bytesToRational}) and convert integers of
+ * several sizes into bytes ({@link shortToBytes} and {@link
+ * longToBytes}).
+ *
+ * All the methods are static and they all rely on an argument that
+ * specifies the byte order to be used, this must be one of the class
+ * constants {@link LITTLE_ENDIAN} or {@link BIG_ENDIAN}.  These
+ * constants will be referred to as the pseudo type PelByteOrder
+ * throughout the documentation.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelConvert {
+
+  /**
+   * Little-endian (Intel) byte order.
+   *
+   * Data stored in little-endian byte order store the least
+   * significant byte first, so the number 0x12345678 becomes 0x78
+   * 0x56 0x34 0x12 when stored with little-endian byte order.
+   */
+  const LITTLE_ENDIAN = true;
+
+  /**
+   * Big-endian (Motorola) byte order.
+   *
+   * Data stored in big-endian byte order store the most significant
+   * byte first, so the number 0x12345678 becomes 0x12 0x34 0x56 0x78
+   * when stored with big-endian byte order.
+   */
+  const BIG_ENDIAN = false;
+
+
+  /**
+   * Convert an unsigned short into two bytes.
+   *
+   * @param int the unsigned short that will be converted.  The lower
+   * two bytes will be extracted regardless of the actual size passed.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   *
+   * @return string the bytes representing the unsigned short.
+   */
+  static function shortToBytes($value, $endian) {
+    if ($endian == self::LITTLE_ENDIAN)
+      return chr($value) . chr($value >> 8);
+    else
+      return chr($value >> 8) . chr($value);
+  }
+
+
+  /**
+   * Convert a signed short into two bytes.
+   *
+   * @param int the signed short that will be converted.  The lower
+   * two bytes will be extracted regardless of the actual size passed.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   *
+   * @return string the bytes representing the signed short.
+   */
+  static function sShortToBytes($value, $endian) {
+    /* We can just use shortToBytes, since signed shorts fits well
+     * within the 32 bit signed integers used in PHP. */
+    return self::shortToBytes($value, $endian);
+  }
+  
+
+  /**
+   * Convert an unsigned long into four bytes.
+   *
+   * Because PHP limits the size of integers to 32 bit signed, one
+   * cannot really have an unsigned integer in PHP.  But integers
+   * larger than 2^31-1 will be promoted to 64 bit signed floating
+   * point numbers, and so such large numbers can be handled too.
+   *
+   * @param int the unsigned long that will be converted.  The
+   * argument will be treated as an unsigned 32 bit integer and the
+   * lower four bytes will be extracted.  Treating the argument as an
+   * unsigned integer means that the absolute value will be used.  Use
+   * {@link sLongToBytes} to convert signed integers.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   *
+   * @return string the bytes representing the unsigned long.
+   */
+  static function longToBytes($value, $endian) {
+    /* We cannot convert the number to bytes in the normal way (using
+     * shifts and modulo calculations) because the PHP operator >> and
+     * function chr() clip their arguments to 2^31-1, which is the
+     * largest signed integer known to PHP.  But luckily base_convert
+     * handles such big numbers. */
+    $hex = str_pad(base_convert($value, 10, 16), 8, '0', STR_PAD_LEFT);
+    if ($endian == self::LITTLE_ENDIAN)
+      return (chr(hexdec($hex{6} . $hex{7})) .
+              chr(hexdec($hex{4} . $hex{5})) .
+              chr(hexdec($hex{2} . $hex{3})) .
+              chr(hexdec($hex{0} . $hex{1})));
+    else
+      return (chr(hexdec($hex{0} . $hex{1})) .
+              chr(hexdec($hex{2} . $hex{3})) .
+              chr(hexdec($hex{4} . $hex{5})) .
+              chr(hexdec($hex{6} . $hex{7})));
+  }
+
+
+  /**
+   * Convert a signed long into four bytes.
+   *
+   * @param int the signed long that will be converted.  The argument
+   * will be treated as a signed 32 bit integer, from which the lower
+   * four bytes will be extracted.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   *
+   * @return string the bytes representing the signed long.
+   */
+  static function sLongToBytes($value, $endian) {
+    /* We can convert the number into bytes in the normal way using
+     * shifts and modulo calculations here (in contrast with
+     * longToBytes) because PHP automatically handles 32 bit signed
+     * integers for us. */
+    if ($endian == self::LITTLE_ENDIAN)
+      return (chr($value) .
+              chr($value >>  8) .
+              chr($value >> 16) .
+              chr($value >> 24));
+    else
+      return (chr($value >> 24) .
+              chr($value >> 16) .
+              chr($value >>  8) .
+              chr($value));
+  }
+
+
+  /**
+   * Extract an unsigned byte from a string of bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The byte found at the offset will be
+   * returned as an integer.  The must be at least one byte available
+   * at offset.
+   *
+   * @return int the unsigned byte found at offset, e.g., an integer
+   * in the range 0 to 255.
+   */
+  static function bytesToByte($bytes, $offset) {
+    return ord($bytes{$offset});
+  }
+
+
+  /**
+   * Extract a signed byte from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The byte found at the offset will be
+   * returned as an integer.  The must be at least one byte available
+   * at offset.
+   *
+   * @return int the signed byte found at offset, e.g., an integer in
+   * the range -128 to 127.
+   */
+  static function bytesToSByte($bytes, $offset) {
+    $n = self::bytesToByte($bytes, $offset);
+    if ($n > 127)
+      return $n - 256;
+    else
+      return $n;
+  }
+
+
+  /**
+   * Extract an unsigned short from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The short found at the offset will be
+   * returned as an integer.  There must be at least two bytes
+   * available beginning at the offset given.
+   *
+   * @return int the unsigned short found at offset, e.g., an integer
+   * in the range 0 to 65535.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToShort($bytes, $offset, $endian) {
+    if ($endian == self::LITTLE_ENDIAN)
+      return (ord($bytes{$offset+1}) * 256 +
+              ord($bytes{$offset}));
+    else
+      return (ord($bytes{$offset})   * 256 +
+              ord($bytes{$offset+1}));
+  }
+
+
+  /**
+   * Extract a signed short from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The short found at offset will be returned
+   * as an integer.  There must be at least two bytes available
+   * beginning at the offset given.
+   *
+   * @return int the signed byte found at offset, e.g., an integer in
+   * the range -32768 to 32767.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToSShort($bytes, $offset, $endian) {
+    $n = self::bytesToShort($bytes, $offset, $endian);
+    if ($n > 32767)
+      return $n - 65536;
+    else
+      return $n;
+  }
+
+
+  /**
+   * Extract an unsigned long from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The long found at offset will be returned
+   * as an integer.  There must be at least four bytes available
+   * beginning at the offset given.
+   *
+   * @return int the unsigned long found at offset, e.g., an integer
+   * in the range 0 to 4294967295.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToLong($bytes, $offset, $endian) {
+    if ($endian == self::LITTLE_ENDIAN)
+      return (ord($bytes{$offset+3}) * 16777216 +
+              ord($bytes{$offset+2}) * 65536    +
+              ord($bytes{$offset+1}) * 256      +
+              ord($bytes{$offset}));
+    else
+      return (ord($bytes{$offset})   * 16777216 +
+              ord($bytes{$offset+1}) * 65536    +
+              ord($bytes{$offset+2}) * 256      +
+              ord($bytes{$offset+3}));
+  }
+
+
+  /**
+   * Extract a signed long from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The long found at offset will be returned
+   * as an integer.  There must be at least four bytes available
+   * beginning at the offset given.
+   *
+   * @return int the signed long found at offset, e.g., an integer in
+   * the range -2147483648 to 2147483647.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToSLong($bytes, $offset, $endian) {
+    $n = self::bytesToLong($bytes, $offset, $endian);
+    if ($n > 2147483647)
+      return $n - 4294967296;
+    else
+      return $n;
+  }
+
+
+  /**
+   * Extract an unsigned rational from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The rational found at offset will be
+   * returned as an array.  There must be at least eight bytes
+   * available beginning at the offset given.
+   *
+   * @return array the unsigned rational found at offset, e.g., an
+   * array with two integers in the range 0 to 4294967295.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToRational($bytes, $offset, $endian) {
+    return array(self::bytesToLong($bytes, $offset, $endian),
+                 self::bytesToLong($bytes, $offset+4, $endian));
+  }
+
+
+  /**
+   * Extract a signed rational from bytes.
+   *
+   * @param string the bytes.
+   *
+   * @param int the offset.  The rational found at offset will be
+   * returned as an array.  There must be at least eight bytes
+   * available beginning at the offset given.
+   *
+   * @return array the signed rational found at offset, e.g., an array
+   * with two integers in the range -2147483648 to 2147483647.
+   *
+   * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link
+   * BIG_ENDIAN}.
+   */
+  static function bytesToSRational($bytes, $offset, $endian) {
+    return array(self::bytesToSLong($bytes, $offset, $endian),
+                 self::bytesToSLong($bytes, $offset+4, $endian));
+  }
+
+
+  /**
+   * Format bytes for dumping.
+   *
+   * This method is for debug output, it will format a string as a
+   * hexadecimal dump suitable for display on a terminal.  The output
+   * is printed directly to standard out.
+   *
+   * @param string the bytes that will be dumped.
+   *
+   * @param int the maximum number of bytes to dump.  If this is left
+   * out (or left to the default of 0), then the entire string will be
+   * dumped.
+   */
+  static function bytesToDump($bytes, $max = 0) {
+    $s = strlen($bytes);
+
+    if ($max > 0)
+      $s = min($max, $s);
+
+    $line = 24;
+
+    for ($i = 0; $i < $s; $i++) {
+      printf('%02X ', ord($bytes{$i}));
+      
+      if (($i+1) % $line == 0)
+        print("\n");
+    }
+    print("\n");
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryUndefined.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryUndefined.php
--- ./PEL/PelEntryUndefined.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryUndefined.php	2011/09/04 21:16:14
@@ -0,0 +1,416 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryUndefined.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to hold data for Exif tags of format undefined.
+ *
+ * This file contains the base class {@link PelEntryUndefined} and
+ * the subclasses {@link PelEntryUserComment} which should be used
+ * to manage the {@link PelTag::USER_COMMENT} tag, and {@link
+ * PelEntryVersion} which is used to manage entries with version
+ * information.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntry.php');
+/**#@-*/
+
+
+/**
+ * Class for holding data of any kind.
+ *
+ * This class can hold bytes of undefined format.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryUndefined extends PelEntry {
+
+  /**
+   * Make a new PelEntry that can hold undefined data.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag},
+   * e.g., {@link PelTag::SCENE_TYPE}, {@link
+   * PelTag::MAKER_NOTE} or any other tag with format {@link
+   * PelFormat::UNDEFINED}.
+   *
+   * @param string the data that this entry will be holding.  Since
+   * the format is undefined, no checking will be done on the data.
+   */
+  function __construct($tag, $data = '') {
+    $this->tag        = $tag;
+    $this->format     = PelFormat::UNDEFINED;
+    $this->setValue($data);
+  }
+
+
+  /**
+   * Set the data of this undefined entry.
+   *
+   * @param string the data that this entry will be holding.  Since
+   * the format is undefined, no checking will be done on the data.
+   */
+  function setValue($data) {
+    $this->components = strlen($data);
+    $this->bytes      = $data;
+  }
+
+
+  /**
+   * Get the data of this undefined entry.
+   *
+   * @return string the data that this entry is holding.
+   */
+  function getValue() {
+    return $this->bytes;
+  }
+
+
+  /**
+   * Get the value of this entry as text.
+   *
+   * The value will be returned in a format suitable for presentation.
+   *
+   * @param boolean some values can be returned in a long or more
+   * brief form, and this parameter controls that.
+   *
+   * @return string the value as text.
+   */
+  function getText($brief = false) {
+    switch ($this->tag) {
+    case PelTag::FILE_SOURCE:
+      //CC (e->components, 1, v);
+      switch (ord($this->bytes{0})) {
+      case 0x03:
+        return 'DSC';
+      default:
+        return sprintf('0x%02X', ord($this->bytes{0}));
+      }
+   
+    case PelTag::SCENE_TYPE:
+      //CC (e->components, 1, v);
+      switch (ord($this->bytes{0})) {
+      case 0x01:
+        return 'Directly photographed';
+      default:
+        return sprintf('0x%02X', ord($this->bytes{0}));
+      }
+   
+    case PelTag::COMPONENTS_CONFIGURATION:
+      //CC (e->components, 4, v);
+      $v = '';
+      for ($i = 0; $i < 4; $i++) {
+        switch (ord($this->bytes{$i})) {
+        case 0:
+          $v .= '-';
+          break;
+        case 1:
+          $v .= 'Y';
+          break;
+        case 2:
+          $v .= 'Cb';
+          break;
+        case 3:
+          $v .= 'Cr';
+          break;
+        case 4:
+          $v .= 'R';
+          break;
+        case 5:
+          $v .= 'G';
+          break;
+        case 6:
+          $v .= 'B';
+          break;
+        default:
+          $v .= 'reserved';
+          break;
+        }
+        if ($i < 3) $v .= ' ';
+      }
+      return $v;
+
+    case PelTag::MAKER_NOTE:
+      // TODO: handle maker notes.
+      return $this->components . ' bytes unknown MakerNote data';
+
+    default:
+      return '(undefined)';
+    }
+  }
+
+}
+
+
+/**
+ * Class for a user comment.
+ *
+ * This class is used to hold user comments, which can come in several
+ * different character encodings.  The Exif standard specifies a
+ * certain format of the {@link PelTag::USER_COMMENT user comment
+ * tag}, and this class will make sure that the format is kept.
+ *
+ * The most basic use of this class simply stores an ASCII encoded
+ * string for later retrieval using {@link getValue}:
+ *
+ * <code>
+ * $entry = new PelEntryUserComment('An ASCII string');
+ * echo $entry->getValue();
+ * </code>
+ *
+ * The string can be encoded with a different encoding, and if so, the
+ * encoding must be given using the second argument.  The Exif
+ * standard specifies three known encodings: 'ASCII', 'JIS', and
+ * 'Unicode'.  If the user comment is encoded using a character
+ * encoding different from the tree known encodings, then the empty
+ * string should be passed as encoding, thereby specifying that the
+ * encoding is undefined.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryUserComment extends PelEntryUndefined {
+
+  /**
+   * The user comment.
+   *
+   * @var string
+   */
+  private $comment;
+
+  /**
+   * The encoding.
+   *
+   * This should be one of 'ASCII', 'JIS', 'Unicode', or ''.
+   *
+   * @var string
+   */
+  private $encoding;
+
+  /**
+   * Make a new entry for holding a user comment.
+   *
+   * @param string the new user comment.
+   *
+   * @param string the encoding of the comment.  This should be either
+   * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an
+   * undefined encoding.
+   */
+  function __construct($comment = '', $encoding = 'ASCII') {
+    parent::__construct(PelTag::USER_COMMENT);
+    $this->setValue($comment, $encoding);
+  }
+
+  
+  /**
+   * Set the user comment.
+   *
+   * @param string the new user comment.
+   *
+   * @param string the encoding of the comment.  This should be either
+   * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an
+   * unknown encoding.
+   */
+  function setValue($comment = '', $encoding = 'ASCII') {
+    $this->comment  = $comment;
+    $this->encoding = $encoding;
+    parent::setValue(str_pad($encoding, 8, chr(0)) . $comment);
+  }
+
+
+  /**
+   * Returns the user comment.
+   *
+   * The comment is returned with the same character encoding as when
+   * it was set using {@link setValue} or {@link __construct the
+   * constructor}.
+   *
+   * @return string the user comment.
+   */
+  function getValue() {
+    return $this->comment;
+  }
+
+
+  /**
+   * Returns the encoding.
+   *
+   * @return string the encoding of the user comment.
+   */
+  function getEncoding() {
+    return $this->encoding;
+  }
+
+
+  /**
+   * Returns the user comment.
+   *
+   * @return string the user comment.
+   */
+  function getText($brief = false) {
+    return $this->comment;
+  }
+
+}
+
+
+/**
+ * Class to hold version information.
+ *
+ * There are three Exif entries that hold version information: the
+ * {@link PelTag::EXIF_VERSION}, {@link
+ * PelTag::FLASH_PIX_VERSION}, and {@link
+ * PelTag::INTEROPERABILITY_VERSION} tags.  This class manages
+ * those tags.
+ *
+ * The class is used in a very straight-forward way:
+ * <code>
+ * $entry = new PelEntryVersion(PelTag::EXIF_VERSION, 2.2);
+ * </code>
+ * This creates an entry for an file complying to the Exif 2.2
+ * standard.  It is easy to test for standards level of an unknown
+ * entry:
+ * <code>
+ * if ($entry->getTag() == PelTag::EXIF_VERSION &&
+ *     $entry->getValue() > 2.0) {
+ *   echo 'Recent Exif version.';
+ * }
+ * </code>
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryVersion extends PelEntryUndefined {
+
+  /**
+   * The version held by this entry.
+   *
+   * @var float
+   */
+  private $version;
+
+
+  /**
+   * Make a new entry for holding a version.
+   *
+   * @param PelTag the tag.  This should be one of {@link
+   * PelTag::EXIF_VERSION}, {@link PelTag::FLASH_PIX_VERSION},
+   * or {@link PelTag::INTEROPERABILITY_VERSION}.
+   *
+   * @param float the version.  The size of the entries leave room for
+   * exactly four digits: two digits on either side of the decimal
+   * point.
+   */
+  function __construct($tag, $version = 0.0) {
+    parent::__construct($tag);
+    $this->setValue($version);
+  }
+
+
+  /**
+   * Set the version held by this entry.
+   *
+   * @param float the version.  The size of the entries leave room for
+   * exactly four digits: two digits on either side of the decimal
+   * point.
+   */
+  function setValue($version = 0.0) {
+    $this->version = $version;
+    $major = floor($version);
+    $minor = ($version - $major)*100;
+    parent::setValue(sprintf('%02.0f%02.0f', $major, $minor));
+  }
+
+
+  /**
+   * Return the version held by this entry.
+   *
+   * @return float the version.  This will be the same as the value
+   * given to {@link setValue} or {@link __construct the
+   * constructor}.
+   */
+  function getValue() {
+    return $this->version;
+  }
+
+ 
+  /**
+   * Return a text string with the version.
+   *
+   * @param boolean controls if the output should be brief.  Brief
+   * output omits the word 'Version' so the result is just 'Exif x.y'
+   * instead of 'Exif Version x.y' if the entry holds information
+   * about the Exif version --- the output for FlashPix is similar.
+   *
+   * @return string the version number with the type of the tag,
+   * either 'Exif' or 'FlashPix'.
+   */
+  function getText($brief = false) {
+    $v = $this->version;
+
+    /* Versions numbers like 2.0 would be output as just 2 if we don't
+     * add the '.0' ourselves. */
+    if (floor($this->version) == $this->version)
+      $v .= '.0';
+
+    switch ($this->tag) {
+    case PelTag::EXIF_VERSION:
+      if ($brief)
+        return Pel::fmt('Exif %s', $v);
+      else
+        return Pel::fmt('Exif Version %s', $v);
+      
+    case PelTag::FLASH_PIX_VERSION:
+      if ($brief)
+        return Pel::fmt('FlashPix %s', $v);
+      else
+        return Pel::fmt('FlashPix Version %s', $v);
+      
+    case PelTag::INTEROPERABILITY_VERSION:
+      if ($brief)
+        return Pel::fmt('Interoperability %s', $v);
+      else
+        return Pel::fmt('Interoperability Version %s', $v);
+    }
+
+    if ($brief)
+      return $v;
+    else
+      return Pel::fmt('Version %s', $v);
+    
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelTiff.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelTiff.php
--- ./PEL/PelTiff.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelTiff.php	2011/09/04 21:16:14
@@ -0,0 +1,296 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelTiff.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes for dealing with TIFF data.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelDataWindow.php');
+require_once('PelIfd.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Class for handling TIFF data.
+ *
+ * Exif data is actually an extension of the TIFF file format.  TIFF
+ * images consist of a number of {@link PelIfd Image File Directories}
+ * (IFDs), each containing a number of {@link PelEntry entries}.  The
+ * IFDs are linked to each other --- one can get hold of the first one
+ * with the {@link getIfd()} method.
+ *
+ * To parse a TIFF image for Exif data one would do:
+ *
+ * <code> 
+ * $tiff = new PelTiff($data);
+ * $ifd0 = $tiff->getIfd();
+ * $exif = $ifd0->getSubIfd(PelIfd::EXIF);
+ * $ifd1 = $ifd0->getNextIfd();
+ * </code>
+ *
+ * Should one have some image data of an unknown type, then the {@link
+ * PelTiff::isValid()} function is handy: it will quickly test if the
+ * data could be valid TIFF data.  The {@link PelJpeg::isValid()}
+ * function does the same for JPEG images.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelTiff {
+
+  /**
+   * TIFF header.
+   *
+   * This must follow after the two bytes indicating the byte order.
+   */
+  const TIFF_HEADER = 0x002A;
+
+  /**
+   * The first Image File Directory, if any.
+   *
+   * If set, then the type of the IFD must be {@link PelIfd::IFD0}.
+   *
+   * @var PelIfd
+   */
+  private $ifd = null;
+
+
+  /**
+   * Construct a new object for holding TIFF data.
+   *
+   * The new object will be empty (with no {@link PelIfd}) unless an
+   * argument is given from which it can initialize itself. This can
+   * either be the filename of a TIFF image or a {@link PelDataWindow}
+   * object.
+   *
+   * Use {@link setIfd()} to explicitly set the IFD.
+   */
+  function __construct($data = false) {
+    if ($data === false)
+      return;
+
+    if (is_string($data)) {
+      Pel::debug('Initializing PelTiff object from %s', $data);
+      $this->loadFile($data);
+    } elseif ($data instanceof PelDataWindow) {
+      Pel::debug('Initializing PelTiff object from PelDataWindow.');
+      $this->load($data);
+    } else {
+      throw new PelInvalidArgumentException('Bad type for $data: %s', 
+                                            gettype($data));
+    }
+  }
+
+
+  /**
+   * Load TIFF data.
+   *
+   * The data given will be parsed and an internal tree representation
+   * will be built.  If the data cannot be parsed correctly, a {@link
+   * PelInvalidDataException} is thrown, explaining the problem.
+   *
+   * @param PelDataWindow the data from which the object will be
+   * constructed.  This should be valid TIFF data, coming either
+   * directly from a TIFF image or from the Exif data in a JPEG image.
+   */
+  function load(PelDataWindow $d) {
+    Pel::debug('Parsing %d bytes of TIFF data...', $d->getSize());
+
+    /* There must be at least 8 bytes available: 2 bytes for the byte
+     * order, 2 bytes for the TIFF header, and 4 bytes for the offset
+     * to the first IFD. */
+    if ($d->getSize() < 8)
+      throw new PelInvalidDataException('Expected at least 8 bytes of TIFF ' .
+                                        'data, found just %d bytes.',
+                                        $d->getSize());
+
+    /* Byte order */
+    if ($d->strcmp(0, 'II')) {
+      Pel::debug('Found Intel byte order');
+      $d->setByteOrder(PelConvert::LITTLE_ENDIAN);
+    } elseif ($d->strcmp(0, 'MM')) {
+      Pel::debug('Found Motorola byte order');
+      $d->setByteOrder(PelConvert::BIG_ENDIAN);
+    } else {
+      throw new PelInvalidDataException('Unknown byte order found in TIFF ' .
+                                        'data: 0x%2X%2X',
+                                        $d->getByte(0), $d->getByte(1));
+    }
+    
+    /* Verify the TIFF header */
+    if ($d->getShort(2) != self::TIFF_HEADER)
+      throw new PelInvalidDataException('Missing TIFF magic value.');
+
+    /* IFD 0 offset */
+    $offset = $d->getLong(4);
+    Pel::debug('First IFD at offset %d.', $offset);
+
+    if ($offset > 0) {
+      /* Parse the first IFD, this will automatically parse the
+       * following IFDs and any sub IFDs. */
+      $this->ifd = new PelIfd(PelIfd::IFD0);
+      $this->ifd->load($d, $offset);
+    }
+  }
+
+
+  /**
+   * Load data from a file into a TIFF object.
+   *
+   * @param string the filename.  This must be a readable file.
+   */
+  function loadFile($filename) {
+    $this->load(new PelDataWindow(file_get_contents($filename)));
+  }
+
+
+  /**
+   * Set the first IFD.
+   *
+   * @param PelIfd the new first IFD, which must be of type {@link
+   * PelIfd::IFD0}.
+   */
+  function setIfd(PelIfd $ifd) {
+    if ($ifd->getType() != PelIfd::IFD0)
+      throw new PelInvalidDataException('Invalid type of IFD: %d, expected %d.',
+                                        $ifd->getType(), PelIfd::IFD0);
+
+    $this->ifd = $ifd;
+  }
+
+
+  /**
+   * Return the first IFD.
+   *
+   * @return PelIfd the first IFD contained in the TIFF data, if any.
+   * If there is no IFD null will be returned.
+   */
+  function getIfd() {
+    return $this->ifd;
+  }
+
+
+  /**
+   * Turn this object into bytes.
+   *
+   * TIFF images can have {@link PelConvert::LITTLE_ENDIAN
+   * little-endian} or {@link PelConvert::BIG_ENDIAN big-endian} byte
+   * order, and so this method takes an argument specifying that.
+   *
+   * @param PelByteOrder the desired byte order of the TIFF data.
+   * This should be one of {@link PelConvert::LITTLE_ENDIAN} or {@link
+   * PelConvert::BIG_ENDIAN}.
+   *
+   * @return string the bytes representing this object.
+   */
+  function getBytes($order = PelConvert::LITTLE_ENDIAN) {
+    if ($order == PelConvert::LITTLE_ENDIAN)
+      $bytes = 'II';
+    else
+      $bytes = 'MM';
+    
+    /* TIFF magic number --- fixed value. */
+    $bytes .= PelConvert::shortToBytes(self::TIFF_HEADER, $order);
+
+    if ($this->ifd != null) {
+      /* IFD 0 offset.  We will always start IDF 0 at an offset of 8
+       * bytes (2 bytes for byte order, another 2 bytes for the TIFF
+       * header, and 4 bytes for the IFD 0 offset make 8 bytes
+       * together).
+       */
+      $bytes .= PelConvert::longToBytes(8, $order);
+    
+      /* The argument specifies the offset of this IFD.  The IFD will
+       * use this to calculate offsets from the entries to their data,
+       * all those offsets are absolute offsets counted from the
+       * beginning of the data. */
+      $bytes .= $this->ifd->getBytes(8, $order);
+    } else {
+      $bytes .= PelConvert::longToBytes(0, $order);
+    }
+
+    return $bytes;
+  }
+
+
+  /**
+   * Return a string representation of this object.
+   *
+   * @return string a string describing this object.  This is mostly useful
+   * for debugging.
+   */
+  function __toString() {
+    $str = Pel::fmt("Dumping TIFF data...\n");
+    if ($this->ifd != null)
+      $str .= $this->ifd->__toString();
+
+    return $str;
+  }
+
+
+  /**
+   * Check if data is valid TIFF data.
+   *
+   * This will read just enough data from the data window to determine
+   * if the data could be a valid TIFF data.  This means that the
+   * check is more like a heuristic than a rigorous check.
+   *
+   * @param PelDataWindow the bytes that will be examined.
+   *
+   * @return boolean true if the data looks like valid TIFF data,
+   * false otherwise.
+   *
+   * @see PelJpeg::isValid()
+   */
+  static function isValid(PelDataWindow $d) {
+    /* First check that we have enough data. */
+    if ($d->getSize() < 8)
+      return false;
+
+    /* Byte order */
+    if ($d->strcmp(0, 'II')) {
+      $d->setByteOrder(PelConvert::LITTLE_ENDIAN);
+    } elseif ($d->strcmp(0, 'MM')) {
+      Pel::debug('Found Motorola byte order');
+      $d->setByteOrder(PelConvert::BIG_ENDIAN);
+    } else {
+      return false;
+    }
+    
+    /* Verify the TIFF header */
+    return $d->getShort(2) == self::TIFF_HEADER;
+  }
+
+}
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelJpegComment.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelJpegComment.php
--- ./PEL/PelJpegComment.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelJpegComment.php	2011/09/04 21:16:14
@@ -0,0 +1,121 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2005, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelJpegComment.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Class for dealing with JPEG comments.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelJpegContent.php');
+/**#@-*/
+
+
+/**
+ * Class representing JPEG comments.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelJpegComment extends PelJpegContent {
+
+  /**
+   * The comment.
+   *
+   * @var string
+   */
+  private $comment = '';
+
+  /**
+   * Construct a new JPEG comment.
+   *
+   * The new comment will contain the string given.
+   */
+  function __construct($comment = '') {
+    $this->comment = $comment;
+  }
+
+
+  /**
+   * Load and parse data.
+   *
+   * This will load the comment from the data window passed.
+   */
+  function load(PelDataWindow $d) {
+    $this->comment = $d->getBytes();
+  }
+
+
+  /**
+   * Update the value with a new comment.
+   *
+   * Any old comment will be overwritten.
+   *
+   * @param string the new comment.
+   */
+  function setValue($comment) {
+    $this->comment = $comment;
+  }
+
+
+  /**
+   * Get the comment.
+   *
+   * @return string the comment.
+   */
+  function getValue() {
+    return $this->comment;
+  }
+
+
+  /**
+   * Turn this comment into bytes.
+   *
+   * @return string bytes representing this comment.
+   */
+  function getBytes() {
+    return $this->comment;
+  }
+
+
+  /**
+   * Return a string representation of this object.
+   *
+   * @return string the same as {@link getValue()}.
+   */
+  function __toString() {
+    return $this->getValue();
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntry.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntry.php
--- ./PEL/PelEntry.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntry.php	2011/09/04 21:16:14
@@ -0,0 +1,382 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntry.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes for dealing with Exif entries.
+ *
+ * This file defines two exception classes and the abstract class
+ * {@link PelEntry} which provides the basic methods that all Exif
+ * entries will have.  All Exif entries will be represented by
+ * descendants of the {@link PelEntry} class --- the class itself is
+ * abstract and so it cannot be instantiated.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelException.php');
+require_once('PelFormat.php');
+require_once('PelTag.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Exception indicating a problem with an entry.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelEntryException extends PelException {
+
+  /**
+   * The IFD type (if known).
+   *
+   * @var int
+   */
+  protected $type;
+
+  /**
+   * The tag of the entry (if known).
+   *
+   * @var PelTag
+   */
+  protected $tag;
+
+  /**
+   * Get the IFD type associated with the exception.
+   *
+   * @return int one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1},
+   * {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link
+   * PelIfd::INTEROPERABILITY}.  If no type is set, null is returned.
+   */
+  function getIfdType() {
+    return $this->type;
+  }
+
+
+  /**
+   * Get the tag associated with the exception.
+   *
+   * @return PelTag the tag.  If no tag is set, null is returned.
+   */
+  function getTag() {
+    return $this->tag;
+  }
+
+}
+
+
+/**
+ * Exception indicating that an unexpected format was found.
+ *
+ * The documentation for each tag in {@link PelTag} will detail any
+ * constrains.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelUnexpectedFormatException extends PelEntryException {
+
+  /**
+   * Construct a new exception indicating an invalid format.
+   *
+   * @param int the type of IFD.
+   *
+   * @param PelTag the tag for which the violation was found.
+   *
+   * @param PelFormat the format found.
+   *
+   * @param PelFormat the expected format.
+   */
+  function __construct($type, $tag, $found, $expected) {
+    parent::__construct('Unexpected format found for %s tag: PelFormat::%s. ' .
+                        'Expected PelFormat::%s instead.',
+                        PelTag::getName($type, $tag),
+                        strtoupper(PelFormat::getName($found)),
+                        strtoupper(PelFormat::getName($expected)));
+    $this->tag  = $tag;
+    $this->type = $type;
+  }
+}
+
+
+/**
+ * Exception indicating that an unexpected number of components was
+ * found.
+ *
+ * Some tags have strict limits as to the allowed number of
+ * components, and this exception is thrown if the data violates such
+ * a constraint.  The documentation for each tag in {@link PelTag}
+ * explains the expected number of components.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelWrongComponentCountException extends PelEntryException {
+
+  /**
+   * Construct a new exception indicating a wrong number of
+   * components.
+   *
+   * @param int the type of IFD.
+   *
+   * @param PelTag the tag for which the violation was found.
+   *
+   * @param int the number of components found.
+   *
+   * @param int the expected number of components.
+   */
+  function __construct($type, $tag, $found, $expected) {
+    parent::__construct('Wrong number of components found for %s tag: %d. ' .
+                        'Expected %d.',
+                        PelTag::getName($type, $tag), $found, $expected);
+    $this->tag  = $tag;
+    $this->type = $type;
+  }
+}
+
+
+/**
+ * Common ancestor class of all {@link PelIfd} entries.
+ *
+ * As this class is abstract you cannot instantiate objects from it.
+ * It only serves as a common ancestor to define the methods common to
+ * all entries.  The most important methods are {@link getValue()} and
+ * {@link setValue()}, both of which is abstract in this class.  The
+ * descendants will give concrete implementations for them.
+ *
+ * If you have some data coming from an image (some raw bytes), then
+ * the static method {@link newFromData()} is helpful --- it will look
+ * at the data and give you a proper decendent of {@link PelEntry}
+ * back.
+ *
+ * If you instead want to have an entry for some data which take the
+ * form of an integer, a string, a byte, or some other PHP type, then
+ * don't use this class.  You should instead create an object of the
+ * right subclass ({@link PelEntryShort} for short integers, {@link
+ * PelEntryAscii} for strings, and so on) directly.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+abstract class PelEntry {
+
+  /**
+   * Type of IFD containing this tag.
+   *
+   * This must be one of the constants defined in {@link PelIfd}:
+   * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1}
+   * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif
+   * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link
+   * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD.
+   *
+   * @var int
+   */
+  protected $ifd_type;
+
+  /**
+   * The bytes representing this entry.
+   *
+   * Subclasses must either override {@link getBytes()} or, if
+   * possible, maintain this property so that it always contains a
+   * true representation of the entry.
+   *
+   * @var string
+   */
+  protected $bytes = '';
+
+  /**
+   * The {@link PelTag} of this entry.
+   *
+   * @var PelTag
+   */
+  protected $tag;
+
+  /**
+   * The {@link PelFormat} of this entry.
+   *
+   * @var PelFormat
+   */
+  protected $format;
+
+  /**
+   * The number of components of this entry.
+   *
+   * @var int
+   */
+  protected $components;
+
+
+  /**
+   * Return the tag of this entry.
+   *
+   * @return PelTag the tag of this entry.
+   */
+  function getTag() {
+    return $this->tag;
+  }
+
+
+  /**
+   * Return the type of IFD which holds this entry.
+   *
+   * @return int one of the constants defined in {@link PelIfd}:
+   * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1}
+   * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif
+   * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link
+   * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD.
+   */
+  function getIfdType() {
+    return $this->ifd_type;
+  }
+
+
+  /**
+   * Update the IFD type.
+   *
+   * @param int must be one of the constants defined in {@link
+   * PelIfd}: {@link PelIfd::IFD0} for the main image IFD, {@link
+   * PelIfd::IFD1} for the thumbnail image IFD, {@link PelIfd::EXIF}
+   * for the Exif sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or
+   * {@link PelIfd::INTEROPERABILITY} for the interoperability
+   * sub-IFD.
+   */
+  function setIfdType($type) {
+    $this->ifd_type = $type;
+  }
+
+
+  /**
+   * Return the format of this entry.
+   *
+   * @return PelFormat the format of this entry.
+   */
+  function getFormat() {
+    return $this->format;
+  }
+
+
+  /**
+   * Return the number of components of this entry.
+   *
+   * @return int the number of components of this entry.
+   */
+  function getComponents() {
+    return $this->components;
+  }
+
+
+  /**
+   * Turn this entry into bytes.
+   *
+   * @param PelByteOrder the desired byte order, which must be either
+   * {@link Convert::LITTLE_ENDIAN} or {@link Convert::BIG_ENDIAN}.
+   *
+   * @return string bytes representing this entry.
+   */
+  function getBytes($o) {
+    return $this->bytes;
+  }
+
+
+  /**
+   * Get the value of this entry as text.
+   *
+   * The value will be returned in a format suitable for presentation,
+   * e.g., rationals will be returned as 'x/y', ASCII strings will be
+   * returned as themselves etc.
+   *
+   * @param boolean some values can be returned in a long or more
+   * brief form, and this parameter controls that.
+   *
+   * @return string the value as text.
+   */
+  abstract function getText($brief = false);
+
+
+  /**
+   * Get the value of this entry.
+   *
+   * The value returned will generally be the same as the one supplied
+   * to the constructor or with {@link setValue()}.  For a formatted
+   * version of the value, one should use {@link getText()} instead.
+   *
+   * @return mixed the unformatted value.
+   */
+  abstract function getValue();
+
+
+  /**
+   * Set the value of this entry.
+   *
+   * The value should be in the same format as for the constructor.
+   *
+   * @param mixed the new value.
+   *
+   * @abstract
+   */
+  function setValue($value) {
+    /* This (fake) abstract method is here to make it possible for the
+     * documentation to refer to PelEntry::setValue().
+     *
+     * It cannot declared abstract in the proper PHP way, for then PHP
+     * wont allow subclasses to define it with two arguments (which is
+     * what PelEntryCopyright does).
+     */
+    throw new PelException('setValue() is abstract.');
+  }
+
+
+  /**
+   * Turn this entry into a string.
+   *
+   * @return string a string representation of this entry.  This is
+   * mostly for debugging.
+   */
+  function __toString() {
+    $str = Pel::fmt("  Tag: 0x%04X (%s)\n",
+                    $this->tag, PelTag::getName($this->ifd_type, $this->tag));
+    $str .= Pel::fmt("    Format    : %d (%s)\n",
+                     $this->format, PelFormat::getName($this->format));
+    $str .= Pel::fmt("    Components: %d\n", $this->components);
+    if ($this->getTag() != PelTag::MAKER_NOTE &&
+        $this->getTag() != PelTag::PRINT_IM)
+      $str .= Pel::fmt("    Value     : %s\n", print_r($this->getValue(), true));
+    $str .= Pel::fmt("    Text      : %s\n", $this->getText());
+    return $str;
+  }
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryShort.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryShort.php
--- ./PEL/PelEntryShort.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryShort.php	2011/09/04 21:16:14
@@ -0,0 +1,599 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryShort.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to hold shorts, both signed and unsigned.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntryNumber.php');
+require_once('PelConvert.php');
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Class for holding signed shorts.
+ *
+ * This class can hold shorts, either just a single short or an array
+ * of shorts.  The class will be used to manipulate any of the Exif
+ * tags which has format {@link PelFormat::SHORT} like in this
+ * example:
+ *
+ * <code>
+ * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH);
+ * $w->setValue($w->getValue() / 2);
+ * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT);
+ * $h->setValue($h->getValue() / 2);
+ * </code>
+ *
+ * Here the width and height is updated to 50% of their original
+ * values.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryShort extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold an unsigned short.
+   *
+   * The method accept several integer arguments.  The {@link
+   * getValue} method will always return an array except for when a
+   * single integer argument is given here.
+   *
+   * This means that one can conveniently use objects like this:
+   * <code>
+   * $a = new PelEntryShort(PelTag::EXIF_IMAGE_HEIGHT, 42);
+   * $b = $a->getValue() + 314;
+   * </code>
+   * where the call to {@link getValue} will return an integer
+   * instead of an array with one integer element, which would then
+   * have to be extracted.
+   *
+   * @param PelTag the tag which this entry represents.  This should be
+   * one of the constants defined in {@link PelTag}, e.g., {@link
+   * PelTag::IMAGE_WIDTH}, {@link PelTag::ISO_SPEED_RATINGS},
+   * or any other tag with format {@link PelFormat::SHORT}.
+   *
+   * @param int $value... the short(s) that this entry will
+   * represent.  The argument passed must obey the same rules as the
+   * argument to {@link setValue}, namely that it should be within
+   * range of an unsigned short, that is between 0 and 65535
+   * (inclusive).  If not, then a {@link PelOverFlowException} will be
+   * thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = 0;
+    $this->max    = 65535;
+    $this->format = PelFormat::SHORT;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return PelConvert::shortToBytes($number, $order);
+  }
+
+
+  /**
+   * Get the value of an entry as text.
+   *
+   * The value will be returned in a format suitable for presentation,
+   * e.g., instead of returning '2' for a {@link
+   * PelTag::METERING_MODE} tag, 'Center-Weighted Average' is
+   * returned.
+   *
+   * @param boolean some values can be returned in a long or more
+   * brief form, and this parameter controls that.
+   *
+   * @return string the value as text.
+   */
+  function getText($brief = false) {
+    switch ($this->tag) {
+    case PelTag::METERING_MODE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Unknown');
+      case 1:
+        return Pel::tra('Average');
+      case 2:
+        return Pel::tra('Center-Weighted Average');
+      case 3:
+        return Pel::tra('Spot');
+      case 4:
+        return Pel::tra('Multi Spot');
+      case 5:
+        return Pel::tra('Pattern');
+      case 6:
+        return Pel::tra('Partial');
+      case 255:
+        return Pel::tra('Other');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::COMPRESSION:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 1:
+        return Pel::tra('Uncompressed');
+      case 6:
+        return Pel::tra('JPEG compression');
+      default:
+        return $this->value[0];
+      
+      }
+
+    case PelTag::PLANAR_CONFIGURATION:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 1:
+        return Pel::tra('chunky format');
+      case 2:
+        return Pel::tra('planar format');
+      default:
+        return $this->value[0];
+      }
+      
+    case PelTag::SENSING_METHOD:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 1:
+        return Pel::tra('Not defined');
+      case 2:
+        return Pel::tra('One-chip color area sensor');
+      case 3:
+        return Pel::tra('Two-chip color area sensor');
+      case 4:
+        return Pel::tra('Three-chip color area sensor');
+      case 5:
+        return Pel::tra('Color sequential area sensor');
+      case 7:
+        return Pel::tra('Trilinear sensor');
+      case 8:
+        return Pel::tra('Color sequential linear sensor');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::LIGHT_SOURCE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Unknown');
+      case 1:
+        return Pel::tra('Daylight');
+      case 2:
+        return Pel::tra('Fluorescent');
+      case 3:
+        return Pel::tra('Tungsten (incandescent light)');
+      case 4:
+        return Pel::tra('Flash');
+      case 9:
+        return Pel::tra('Fine weather');
+      case 10:
+        return Pel::tra('Cloudy weather');
+      case 11:
+        return Pel::tra('Shade');
+      case 12:
+        return Pel::tra('Daylight fluorescent');
+      case 13:
+        return Pel::tra('Day white fluorescent');
+      case 14:
+        return Pel::tra('Cool white fluorescent');
+      case 15:
+        return Pel::tra('White fluorescent');
+      case 17:
+        return Pel::tra('Standard light A');
+      case 18:
+        return Pel::tra('Standard light B');
+      case 19:
+        return Pel::tra('Standard light C');
+      case 20:
+        return Pel::tra('D55');
+      case 21:
+        return Pel::tra('D65');
+      case 22:
+        return Pel::tra('D75');
+      case 24:
+        return Pel::tra('ISO studio tungsten');
+      case 255:
+        return Pel::tra('Other');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::FOCAL_PLANE_RESOLUTION_UNIT:
+    case PelTag::RESOLUTION_UNIT:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 2:
+        return Pel::tra('Inch');
+      case 3:
+        return Pel::tra('Centimeter');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::EXPOSURE_PROGRAM:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Not defined');
+      case 1:
+        return Pel::tra('Manual');
+      case 2:
+        return Pel::tra('Normal program');
+      case 3:
+        return Pel::tra('Aperture priority');
+      case 4:
+        return Pel::tra('Shutter priority');
+      case 5:
+        return Pel::tra('Creative program (biased toward depth of field)');
+      case 6:
+        return Pel::tra('Action program (biased toward fast shutter speed)');
+      case 7:
+        return Pel::tra('Portrait mode (for closeup photos with the background out of focus');
+      case 8:
+        return Pel::tra('Landscape mode (for landscape photos with the background in focus');
+      default:
+        return $this->value[0];
+      }
+   
+    case PelTag::ORIENTATION:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 1:
+        return Pel::tra('top - left');
+      case 2:
+        return Pel::tra('top - right');
+      case 3:
+        return Pel::tra('bottom - right');
+      case 4:
+        return Pel::tra('bottom - left');
+      case 5:
+        return Pel::tra('left - top');
+      case 6:
+        return Pel::tra('right - top');
+      case 7:
+        return Pel::tra('right - bottom');
+      case 8:
+        return Pel::tra('left - bottom');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::YCBCR_POSITIONING:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 1:
+        return Pel::tra('centered');
+      case 2:
+        return Pel::tra('co-sited');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::YCBCR_SUB_SAMPLING:
+      //CC (e->components, 2, v);
+      if ($this->value[0] == 2 && $this->value[1] == 1)
+        return 'YCbCr4:2:2';
+      if ($this->value[0] == 2 && $this->value[1] == 2)
+        return 'YCbCr4:2:0';
+      
+      return $this->value[0] . ', ' . $this->value[1];
+   
+    case PelTag::PHOTOMETRIC_INTERPRETATION:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 2:
+        return 'RGB';
+      case 6:
+        return 'YCbCr';
+      default:
+        return $this->value[0];
+      }
+   
+    case PelTag::COLOR_SPACE:
+      //CC (e->components, 1, v); 
+      switch ($this->value[0]) { 
+      case 1:
+        return 'sRGB';
+      case 2:
+        return 'Adobe RGB';
+      case 0xffff:
+        return Pel::tra('Uncalibrated');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::FLASH:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0x0000:
+        return Pel::tra('Flash did not fire.');
+      case 0x0001:
+        return Pel::tra('Flash fired.');
+      case 0x0005:
+        return Pel::tra('Strobe return light not detected.');
+      case 0x0007:
+        return Pel::tra('Strobe return light detected.');
+      case 0x0009:
+        return Pel::tra('Flash fired, compulsory flash mode.');
+      case 0x000d:
+        return Pel::tra('Flash fired, compulsory flash mode, return light not detected.');
+      case 0x000f:
+        return Pel::tra('Flash fired, compulsory flash mode, return light detected.');
+      case 0x0010:
+        return Pel::tra('Flash did not fire, compulsory flash mode.');
+      case 0x0018:
+        return Pel::tra('Flash did not fire, auto mode.');
+      case 0x0019:
+        return Pel::tra('Flash fired, auto mode.');
+      case 0x001d:
+        return Pel::tra('Flash fired, auto mode, return light not detected.');
+      case 0x001f:
+        return Pel::tra('Flash fired, auto mode, return light detected.');
+      case 0x0020:
+        return Pel::tra('No flash function.');
+      case 0x0041:
+        return Pel::tra('Flash fired, red-eye reduction mode.');
+      case 0x0045:
+        return Pel::tra('Flash fired, red-eye reduction mode, return light not detected.');
+      case 0x0047:
+        return Pel::tra('Flash fired, red-eye reduction mode, return light detected.');
+      case 0x0049:
+        return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode.');
+      case 0x004d:
+        return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected.');
+      case 0x004f:
+        return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light detected.');
+      case 0x0058:
+        return Pel::tra('Flash did not fire, auto mode, red-eye reduction mode.');
+      case 0x0059:
+        return Pel::tra('Flash fired, auto mode, red-eye reduction mode.');
+      case 0x005d:
+        return Pel::tra('Flash fired, auto mode, return light not detected, red-eye reduction mode.');
+      case 0x005f:
+        return Pel::tra('Flash fired, auto mode, return light detected, red-eye reduction mode.');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::CUSTOM_RENDERED:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Normal process');
+      case 1:
+        return Pel::tra('Custom process');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::EXPOSURE_MODE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Auto exposure');
+      case 1:
+        return Pel::tra('Manual exposure');
+      case 2:
+        return Pel::tra('Auto bracket');
+      default:
+        return $this->value[0];
+      }
+   
+    case PelTag::WHITE_BALANCE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Auto white balance');
+      case 1:
+        return Pel::tra('Manual white balance');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::SCENE_CAPTURE_TYPE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Standard');
+      case 1:
+        return Pel::tra('Landscape');
+      case 2:
+        return Pel::tra('Portrait');
+      case 3:
+        return Pel::tra('Night scene');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::GAIN_CONTROL:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Normal');
+      case 1:
+        return Pel::tra('Low gain up');
+      case 2:
+        return Pel::tra('High gain up');
+      case 3:
+        return Pel::tra('Low gain down');
+      case 4:
+        return Pel::tra('High gain down');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::SATURATION:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Normal');
+      case 1:
+        return Pel::tra('Low saturation');
+      case 2:
+        return Pel::tra('High saturation');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::CONTRAST:
+    case PelTag::SHARPNESS:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Normal');
+      case 1:
+        return Pel::tra('Soft');
+      case 2:
+        return Pel::tra('Hard');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::SUBJECT_DISTANCE_RANGE:
+      //CC (e->components, 1, v);
+      switch ($this->value[0]) {
+      case 0:
+        return Pel::tra('Unknown');
+      case 1:
+        return Pel::tra('Macro');
+      case 2:
+        return Pel::tra('Close view');
+      case 3:
+        return Pel::tra('Distant view');
+      default:
+        return $this->value[0];
+      }
+
+    case PelTag::SUBJECT_AREA:
+      switch ($this->components) {
+      case 2:
+        return Pel::fmt('(x,y) = (%d,%d)', $this->value[0], $this->value[1]);
+      case 3:
+        return Pel::fmt('Within distance %d of (x,y) = (%d,%d)',
+                        $this->value[0], $this->value[1], $this->value[2]);
+      case 4:
+        return Pel::fmt('Within rectangle (width %d, height %d) around (x,y) = (%d,%d)',
+                        $this->value[0], $this->value[1],
+                        $this->value[2], $this->value[3]);
+        
+      default:
+        return Pel::fmt('Unexpected number of components (%d, expected 2, 3, or 4).', $this->components);
+      }
+
+    default:
+      return parent::getText($brief);
+    }
+  }
+}
+
+
+/**
+ * Class for holding signed shorts.
+ *
+ * This class can hold shorts, either just a single short or an array
+ * of shorts.  The class will be used to manipulate any of the Exif
+ * tags which has format {@link PelFormat::SSHORT}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntrySShort extends PelEntryNumber {
+
+  /**
+   * Make a new entry that can hold a signed short.
+   *
+   * The method accept several integer arguments.  The {@link
+   * getValue} method will always return an array except for when a
+   * single integer argument is given here.
+   *
+   * @param PelTag the tag which this entry represents.  This
+   * should be one of the constants defined in {@link PelTag}
+   * which has format {@link PelFormat::SSHORT}.
+   *
+   * @param int $value... the signed short(s) that this entry will
+   * represent.  The argument passed must obey the same rules as the
+   * argument to {@link setValue}, namely that it should be within
+   * range of a signed short, that is between -32768 to 32767
+   * (inclusive).  If not, then a {@link PelOverFlowException} will be
+   * thrown.
+   */
+  function __construct($tag /* $value... */) {
+    $this->tag    = $tag;
+    $this->min    = -32768;
+    $this->max    = 32767;
+    $this->format = PelFormat::SSHORT;
+
+    $value = func_get_args();
+    array_shift($value);
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  function numberToBytes($number, $order) {
+    return PelConvert::sShortToBytes($number, $order);
+  }
+}
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/Pel.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/Pel.php
--- ./PEL/Pel.php	2011/06/22 21:16:17	1.1
+++ ./PEL/Pel.php	2011/09/04 21:16:14
@@ -0,0 +1,372 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: Pel.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Miscellaneous stuff for the overall behavior of PEL.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+
+/* Initialize Gettext, if available.  This must be done before any
+ * part of PEL calls Pel::tra() or Pel::fmt() --- this is ensured if
+ * every piece of code using those two functions require() this file.
+ *
+ * If Gettext is not available, wrapper functions will be created,
+ * allowing PEL to function, but without any translations.
+ *
+ * The PEL translations are stored in './locale'.  It is important to
+ * use an absolute path here because the lookups will be relative to
+ * the current directory. */
+
+if (function_exists('dgettext')) {
+  bindtextdomain('pel', dirname(__FILE__) . '/locale');
+} else {
+
+  /**
+   * Pretend to lookup a message in a specific domain.
+   *
+   * This is just a stub which will return the original message
+   * untranslated.  The function will only be defined if the Gettext
+   * extension has not already defined it.
+   *
+   * @param string $domain the domain.
+   *
+   * @param string $str the message to be translated.
+   *
+   * @return string the original, untranslated message.
+   */
+  function dgettext($domain, $str) {
+    return $str;
+  }
+}
+
+
+/**
+ * Class with miscellaneous static methods. 
+ *
+ * This class will contain various methods that govern the overall
+ * behavior of PEL.
+ *
+ * Debugging output from PEL can be turned on and off by assigning
+ * true or false to {@link Pel::$debug}.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class Pel {
+
+  /**
+   * Flag for controlling debug information.
+   *
+   * The methods producing debug information ({@link debug()} and
+   * {@link warning()}) will only output something if this variable is
+   * set to true.
+   *
+   * @var boolean
+   */
+  private static $debug = false;
+
+  /**
+   * Flag for strictness of parsing.
+   *
+   * If this variable is set to true, then most errors while loading
+   * images will result in exceptions being thrown.  Otherwise a
+   * warning will be emitted (using {@link Pel::warning}) and the
+   * exceptions will be appended to {@link Pel::$exceptions}.
+   *
+   * Some errors will still be fatal and result in thrown exceptions,
+   * but an effort will be made to skip over as much garbage as
+   * possible.
+   *
+   * @var boolean
+   */
+  private static $strict = false;
+  
+  /**
+   * Stored exceptions.
+   *
+   * When {@link Pel::$strict} is set to false exceptions will be
+   * accumulated here instead of being thrown.
+   */
+  private static $exceptions = array();
+
+  /**
+   * Quality setting for encoding JPEG images.
+   *
+   * This controls the quality used then PHP image resources are
+   * encoded into JPEG images. This happens when you create a
+   * {@link PelJpeg} object based on an image resource.
+   *
+   * The default is 75 for average quality images, but you can change
+   * this to an integer between 0 and 100.
+   *
+   * @var int
+   */
+  private static $quality = 75;
+
+
+  /**
+   * Set the JPEG encoding quality.
+   *
+   * @param int $quality an integer between 0 and 100 with 75 being
+   * average quality and 95 very good quality.
+   */
+  function setJPEGQuality($quality) {
+    self::$quality = $quality;
+  }
+
+
+  /**
+   * Get current setting for JPEG encoding quality.
+   *
+   * @return int the quality.
+   */
+  function getJPEGQuality() {
+    return self::$quality;
+  }
+
+
+  /**
+   * Return list of stored exceptions.
+   *
+   * When PEL is parsing in non-strict mode, it will store most
+   * exceptions instead of throwing them.  Use this method to get hold
+   * of them when a call returns.
+   *
+   * Code for using this could look like this:
+   *
+   * <code>
+   * Pel::setStrictParsing(true);
+   * Pel::clearExceptions();
+   *
+   * $jpeg = new PelJpeg($file);
+   *
+   * // Check for exceptions.
+   * foreach (Pel::getExceptions() as $e) {
+   *     printf("Exception: %s\n", $e->getMessage());
+   *     if ($e instanceof PelEntryException) {
+   *       // Warn about entries that couldn't be loaded.
+   *       printf("Warning: Problem with %s.\n",
+   *              PelTag::getName($e->getType(), $e->getTag()));
+   *     }
+   * }
+   * </code>
+   *
+   * This gives applications total control over the amount of error
+   * messages shown and (hopefully) provides the necessary information
+   * for proper error recovery.
+   *
+   * @return array the exceptions.
+   */
+  static function getExceptions() {
+    return self::$exceptions;
+  }
+
+
+  /**
+   * Clear list of stored exceptions.
+   *
+   * Use this function before a call to some method if you intend to
+   * check for exceptions afterwards.
+   */
+  static function clearExceptions() {
+    self::$exceptions = array();
+  }
+
+
+  /**
+   * Conditionally throw an exception.
+   *
+   * This method will throw the passed exception when strict parsing
+   * in effect (see {@link setStrictParsing()}).  Otherwise the
+   * exception is stored (it can be accessed with {@link
+   * getExceptions()}) and a warning is issued (with {@link
+   * Pel::warning}).
+   *
+   * @param PelException $e the exceptions.
+   */
+  static function maybeThrow(PelException $e) {
+    if (self::$strict) {
+      throw $e;
+    } else {
+      self::$exceptions[] = $e;
+      self::warning('%s (%s:%s)', $e->getMessage(),
+                   basename($e->getFile()), $e->getLine());
+    }
+  }
+
+
+  /**
+   * Enable/disable strict parsing.
+   *
+   * If strict parsing is enabled, then most errors while loading
+   * images will result in exceptions being thrown.  Otherwise a
+   * warning will be emitted (using {@link Pel::warning}) and the
+   * exceptions will be stored for later use via {@link
+   * getExceptions()}.
+   *
+   * Some errors will still be fatal and result in thrown exceptions,
+   * but an effort will be made to skip over as much garbage as
+   * possible.
+   *
+   * @param boolean $flag use true to enable strict parsing, false to
+   * diable.
+   */
+  function setStrictParsing($flag) {
+    self::$strict = $flag;
+  }
+
+
+  /**
+   * Get current setting for strict parsing.
+   *
+   * @return boolean true if strict parsing is in effect, false
+   * otherwise.
+   */
+  function getStrictParsing() {
+    return self::$strict;
+  }
+
+
+  /**
+   * Enable/disable debugging output.
+   *
+   * @param boolean $flag use true to enable debug output, false to
+   * diable.
+   */
+  function setDebug($flag) {
+    self::$debug = $flag;
+  }
+
+
+  /**
+   * Get current setting for debug output.
+   *
+   * @return boolean true if debug is enabled, false otherwise.
+   */
+  function getDebug() {
+    return self::$debug;
+  }
+
+
+  /**
+   * Conditionally output debug information.
+   *
+   * This method works just like printf() except that it always
+   * terminates the output with a newline, and that it only outputs
+   * something if the {@link Pel::$debug} is true.
+   *
+   * @param string $format the format string.
+   *
+   * @param mixed $args,... any number of arguments can be given.  The
+   * arguments will be available for the format string as usual with
+   * sprintf().
+   */
+  static function debug() {
+    if (self::$debug) {
+      $args = func_get_args();
+      $str = array_shift($args);
+      vprintf($str . "\n", $args);
+    }
+  }
+
+  
+  /**
+   * Conditionally output a warning.
+   *
+   * This method works just like printf() except that it prepends the
+   * output with the string 'Warning: ', terminates the output with a
+   * newline, and that it only outputs something if the PEL_DEBUG
+   * defined to some true value.
+   *
+   * @param string $format the format string.
+   *
+   * @param mixed $args,... any number of arguments can be given.  The
+   * arguments will be available for the format string as usual with
+   * sprintf().
+   */
+  static function warning() {
+    if (self::$debug) {
+      $args = func_get_args();
+      $str = array_shift($args);
+      vprintf('Warning: ' . $str . "\n", $args);
+    }
+  }
+
+
+  /**
+   * Translate a string.
+   *
+   * This static function will use Gettext to translate a string.  By
+   * always using this function for static string one is assured that
+   * the translation will be taken from the correct text domain.
+   * Dynamic strings should be passed to {@link fmt} instead.
+   *
+   * @param string the string that should be translated.
+   *
+   * @return string the translated string, or the original string if
+   * no translation could be found.
+   */
+  static function tra($str) {
+    return dgettext('pel', $str);
+  }
+  
+
+  /**
+   * Translate and format a string.
+   *
+   * This static function will first use Gettext to translate a format
+   * string, which will then have access to any extra arguments.  By
+   * always using this function for dynamic string one is assured that
+   * the translation will be taken from the correct text domain.  If
+   * the string is static, use {@link tra} instead as it will be
+   * faster.
+   *
+   * @param string $format the format string.  This will be translated
+   * before being used as a format string.
+   *
+   * @param mixed $args,... any number of arguments can be given.  The
+   * arguments will be available for the format string as usual with
+   * sprintf().
+   *
+   * @return string the translated string, or the original string if
+   * no translation could be found.
+   */
+  static function fmt() {
+    $args = func_get_args();
+    $str = array_shift($args);
+    return vsprintf(dgettext('pel', $str), $args);
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelException.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelException.php
--- ./PEL/PelException.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelException.php	2011/09/04 21:16:14
@@ -0,0 +1,87 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelException.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Standard PEL exception.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**
+ * A printf() capable exception.
+ *
+ * This class is a simple extension of the standard Exception class in
+ * PHP, and all the methods defined there retain their original
+ * meaning.
+ *
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelException extends Exception {
+
+  /**
+   * Construct a new PEL exception.
+   *
+   * @param string $fmt an optional format string can be given.  It
+   * will be used as a format string for vprintf().  The remaining
+   * arguments will be available for the format string as usual with
+   * vprintf().
+   *
+   * @param mixed $args,... any number of arguments to be used with
+   * the format string.
+   */
+  function __construct(/* fmt, args... */) {
+    $args = func_get_args();
+    $fmt = array_shift($args);
+    parent::__construct(vsprintf($fmt, $args));
+  }
+}
+
+
+/**
+ * Exception throw if invalid data is found.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelInvalidDataException extends PelException {}
+
+/**
+ * Exception throw if an invalid argument is passed.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelInvalidArgumentException extends PelException {}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelJpegContent.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelJpegContent.php
--- ./PEL/PelJpegContent.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelJpegContent.php	2011/09/04 21:16:14
@@ -0,0 +1,82 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelJpegContent.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Class representing content in a JPEG file.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelDataWindow.php');
+/**#@-*/
+
+
+/**
+ * Class representing content in a JPEG file.
+ *
+ * A JPEG file consists of a sequence of each of which has an
+ * associated {@link PelJpegMarker marker} and some content.  This
+ * class represents the content, and this basic type is just a simple
+ * holder of such content, represented by a {@link PelDataWindow}
+ * object.  The {@link PelExif} class is an example of more
+ * specialized JPEG content.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelJpegContent {
+  private $data = null;
+
+  /**
+   * Make a new piece of JPEG content.
+   *
+   * @param PelDataWindow the content.
+   */
+  function __construct(PelDataWindow $data) {
+    $this->data = $data;
+  }
+
+  
+  /**
+   * Return the bytes of the content.
+   *
+   * @return string bytes representing this JPEG content.  These bytes
+   * will match the bytes given to {@link __construct the
+   * constructor}.
+   */
+  function getBytes() {
+    return $this->data->getBytes();
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryNumber.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryNumber.php
--- ./PEL/PelEntryNumber.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryNumber.php	2011/09/04 21:16:14
@@ -0,0 +1,309 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryNumber.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Abstract class for numbers.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelException.php');
+require_once('PelEntry.php');
+/**#@-*/
+
+
+/**
+ * Exception cast when numbers overflow.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ * @subpackage Exception
+ */
+class PelOverflowException extends PelException {
+  
+  /**
+   * Construct a new overflow exception.
+   *
+   * @param int the value that is out of range.
+   *
+   * @param int the minimum allowed value.
+   *
+   * @param int the maximum allowed value.
+   */
+  function __construct($v, $min, $max) {
+    parent::__construct('Value %.0f out of range [%.0f, %.0f]',
+                        $v, $min, $max);
+  }
+}
+
+
+/**
+ * Class for holding numbers.
+ *
+ * This class can hold numbers, with range checks.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+abstract class PelEntryNumber extends PelEntry {
+
+  /**
+   * The value held by this entry.
+   *
+   * @var array
+   */
+  protected $value = array();
+
+  /**
+   * The minimum allowed value.
+   *
+   * Any attempt to change the value below this variable will result
+   * in a {@link PelOverflowException} being thrown.
+   *
+   * @var int
+   */
+  protected $min;
+
+  /**
+   * The maximum allowed value.
+   *
+   * Any attempt to change the value over this variable will result in
+   * a {@link PelOverflowException} being thrown.
+   *
+   * @var int
+   */
+  protected $max;
+  
+  /**
+   * The dimension of the number held.
+   *
+   * Normal numbers have a dimension of one, pairs have a dimension of
+   * two, etc.
+   *
+   * @var int
+   */
+  protected $dimension = 1;
+
+
+  /**
+   * Change the value.
+   *
+   * This method can change both the number of components and the
+   * value of the components.  Range checks will be made on the new
+   * value, and a {@link PelOverflowException} will be thrown if the
+   * value is found to be outside the legal range.
+   *
+   * The method accept several number arguments.  The {@link getValue}
+   * method will always return an array except for when a single
+   * number is given here.
+   *
+   * @param int|array $value... the new value(s).  This can be zero or
+   * more numbers, that is, either integers or arrays.  The input will
+   * be checked to ensure that the numbers are within the valid range.
+   * If not, then a {@link PelOverflowException} will be thrown.
+   *
+   * @see getValue
+   */
+  function setValue(/* $value... */) {
+    $value = func_get_args();
+    $this->setValueArray($value);
+  }
+
+
+  /**
+   * Change the value.
+   *
+   * This method can change both the number of components and the
+   * value of the components.  Range checks will be made on the new
+   * value, and a {@link PelOverflowException} will be thrown if the
+   * value is found to be outside the legal range.
+   *
+   * @param array the new values.  The array must contain the new
+   * numbers.
+   *
+   * @see getValue
+   */
+  function setValueArray($value) {
+    foreach ($value as $v)
+      $this->validateNumber($v);
+    
+    $this->components = count($value);
+    $this->value      = $value;
+  }
+
+
+  /**
+   * Return the numeric value held.
+   *
+   * @return int|array this will either be a single number if there is
+   * only one component, or an array of numbers otherwise.
+   */
+  function getValue() {
+    if ($this->components == 1)
+      return $this->value[0];
+    else
+      return $this->value;
+  }
+
+
+  /**
+   * Validate a number.
+   *
+   * This method will check that the number given is within the range
+   * given my {@link getMin()} and {@link getMax()}, inclusive.  If
+   * not, then a {@link PelOverflowException} is thrown.
+   *
+   * @param int|array the number in question.
+   *
+   * @return void nothing, but will throw a {@link
+   * PelOverflowException} if the number is found to be outside the
+   * legal range and {@link Pel::$strict} is true.
+   */
+  function validateNumber($n) {
+    if ($this->dimension == 1) {
+      if ($n < $this->min || $n > $this->max)
+        Pel::maybeThrow(new PelOverflowException($n,
+                                                 $this->min,
+                                                 $this->max));
+    } else {
+      for ($i = 0; $i < $this->dimension; $i++)
+        if ($n[$i] < $this->min || $n[$i] > $this->max)
+          Pel::maybeThrow(new PelOverflowException($n[$i],
+                                                   $this->min,
+                                                   $this->max));
+    }
+  }
+
+
+  /**
+   * Add a number.
+   *
+   * This appends a number to the numbers already held by this entry,
+   * thereby increasing the number of components by one.
+   *
+   * @param int|array the number to be added.
+   */
+  function addNumber($n) {
+    $this->validateNumber($n);
+    $this->value[] = $n;
+    $this->components++;
+  }
+
+
+  /**
+   * Convert a number into bytes.
+   *
+   * The concrete subclasses will have to implement this method so
+   * that the numbers represented can be turned into bytes.
+   *
+   * The method will be called once for each number held by the entry.
+   *
+   * @param int the number that should be converted.
+   *
+   * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and
+   * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order.
+   *
+   * @return string bytes representing the number given.
+   */
+  abstract function numberToBytes($number, $order);
+
+  
+  /**
+   * Turn this entry into bytes.
+   *
+   * @param PelByteOrder the desired byte order, which must be either
+   * {@link PelConvert::LITTLE_ENDIAN} or {@link
+   * PelConvert::BIG_ENDIAN}.
+   *
+   * @return string bytes representing this entry.
+   */
+  function getBytes($o) {
+    $bytes = '';
+    for ($i = 0; $i < $this->components; $i++) {
+      if ($this->dimension == 1) {
+        $bytes .= $this->numberToBytes($this->value[$i], $o);
+      } else {
+        for ($j = 0; $j < $this->dimension; $j++) {
+          $bytes .= $this->numberToBytes($this->value[$i][$j], $o);
+        }
+      }
+    }
+    return $bytes;
+  }
+
+
+  /**
+   * Format a number.
+   *
+   * This method is called by {@link getText} to format numbers.
+   * Subclasses should override this method if they need more
+   * sophisticated behavior than the default, which is to just return
+   * the number as is.
+   *
+   * @param int the number which will be formatted.
+   *
+   * @param boolean it could be that there is both a verbose and a
+   * brief formatting available, and this argument controls that.
+   *
+   * @return string the number formatted as a string suitable for
+   * display.
+   */
+  function formatNumber($number, $brief = false) {
+    return $number;
+  }
+
+
+  /**
+   * Get the numeric value of this entry as text.
+   *
+   * @param boolean use brief output?  The numbers will be separated
+   * by a single space if brief output is requested, otherwise a space
+   * and a comma will be used.
+   *
+   * @return string the numbers(s) held by this entry.
+   */
+  function getText($brief = false) {
+    if ($this->components == 0)
+      return '';
+
+    $str = $this->formatNumber($this->value[0]);
+    for ($i = 1; $i < $this->components; $i++) {
+      $str .= ($brief ? ' ' : ', ');
+      $str .= $this->formatNumber($this->value[$i]);
+    }
+
+    return $str;
+  }
+
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelEntryAscii.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelEntryAscii.php
--- ./PEL/PelEntryAscii.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelEntryAscii.php	2011/09/04 21:16:14
@@ -0,0 +1,561 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelEntryAscii.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes used to hold ASCII strings.
+ *
+ * The classes defined here are to be used for Exif entries holding
+ * ASCII strings, such as {@link PelTag::MAKE}, {@link
+ * PelTag::SOFTWARE}, and {@link PelTag::DATE_TIME}.  For
+ * entries holding normal textual ASCII strings the class {@link
+ * PelEntryAscii} should be used, but for entries holding
+ * timestamps the class {@link PelEntryTime} would be more
+ * convenient instead.  Copyright information is handled by the {@link
+ * PelEntryCopyright} class.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('PelEntry.php');
+/**#@-*/
+
+
+/**
+ * Class for holding a plain ASCII string.
+ *
+ * This class can hold a single ASCII string, and it will be used as in
+ * <code>
+ * $entry = $ifd->getEntry(PelTag::IMAGE_DESCRIPTION);
+ * print($entry->getValue());
+ * $entry->setValue('This is my image.  I like it.');
+ * </code>
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryAscii extends PelEntry {
+
+  /**
+   * The string hold by this entry.
+   *
+   * This is the string that was given to the {@link __construct
+   * constructor} or later to {@link setValue}, without any final NULL
+   * character.
+   *
+   * @var string
+   */
+  private $str;
+
+
+  /**
+   * Make a new PelEntry that can hold an ASCII string.
+   *
+   * @param int the tag which this entry represents.  This should be
+   * one of the constants defined in {@link PelTag}, e.g., {@link
+   * PelTag::IMAGE_DESCRIPTION}, {@link PelTag::MODEL}, or any other
+   * tag with format {@link PelFormat::ASCII}.
+   *
+   * @param string the string that this entry will represent.  The
+   * string must obey the same rules as the string argument to {@link
+   * setValue}, namely that it should be given without any trailing
+   * NULL character and that it must be plain 7-bit ASCII.
+   */
+  function __construct($tag, $str = '') {
+    $this->tag    = $tag;
+    $this->format = PelFormat::ASCII;
+    self::setValue($str);
+  }
+
+
+  /**
+   * Give the entry a new ASCII value.
+   *
+   * This will overwrite the previous value.  The value can be
+   * retrieved later with the {@link getValue} method.
+   *
+   * @param string the new value of the entry.  This should be given
+   * without any trailing NULL character.  The string must be plain
+   * 7-bit ASCII, the string should contain no high bytes.
+   *
+   * @todo Implement check for high bytes?
+   */
+  function setValue($str) {
+    $this->components = strlen($str)+1;
+    $this->str        = $str;
+    $this->bytes      = $str . chr(0x00);
+  }
+
+
+  /**
+   * Return the ASCII string of the entry.
+   *
+   * @return string the string held, without any final NULL character.
+   * The string will be the same as the one given to {@link setValue}
+   * or to the {@link __construct constructor}.
+   */
+  function getValue() {
+    return $this->str;
+  }
+
+
+  /**
+   * Return the ASCII string of the entry.
+   *
+   * This methods returns the same as {@link getValue}.
+   *
+   * @param boolean not used with ASCII entries.
+   *
+   * @return string the string held, without any final NULL character.
+   * The string will be the same as the one given to {@link setValue}
+   * or to the {@link __construct constructor}.
+   */
+  function getText($brief = false) {
+    return $this->str;      
+  }
+
+}
+
+
+/**
+ * Class for holding a date and time.
+ *
+ * This class can hold a timestamp, and it will be used as
+ * in this example where the time is advanced by one week:
+ * <code>
+ * $entry = $ifd->getEntry(PelTag::DATE_TIME_ORIGINAL);
+ * $time = $entry->getValue();
+ * print('The image was taken on the ' . date('jS', $time));
+ * $entry->setValue($time + 7 * 24 * 3600);
+ * </code>
+ *
+ * The example used a standard UNIX timestamp, which is the default
+ * for this class.
+ *
+ * But the Exif format defines dates outside the range of a UNIX
+ * timestamp (about 1970 to 2038) and so you can also get access to
+ * the timestamp in two other formats: a simple string or a Julian Day
+ * Count. Please see the Calendar extension in the PHP Manual for more
+ * information about the Julian Day Count.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryTime extends PelEntryAscii {
+
+  /**
+   * Constant denoting a UNIX timestamp.
+   */
+  const UNIX_TIMESTAMP   = 1;
+  /**
+   * Constant denoting a Exif string.
+   */
+  const EXIF_STRING      = 2;
+  /**
+   * Constant denoting a Julian Day Count.
+   */
+  const JULIAN_DAY_COUNT = 3;
+
+  /**
+   * The Julian Day Count of the timestamp held by this entry.
+   *
+   * This is an integer counting the number of whole days since
+   * January 1st, 4713 B.C. The fractional part of the timestamp held
+   * by this entry is stored in {@link $seconds}.
+   *
+   * @var int
+   */
+  private $day_count;
+
+  /**
+   * The number of seconds into the day of the timestamp held by this
+   * entry.
+   *
+   * The number of whole days is stored in {@link $day_count} and the
+   * number of seconds left-over is stored here.
+   *
+   * @var int
+   */
+  private $seconds;
+
+
+  /**
+   * Make a new entry for holding a timestamp.
+   *
+   * @param int the Exif tag which this entry represents.  There are
+   * only three standard tags which hold timestamp, so this should be
+   * one of the constants {@link PelTag::DATE_TIME}, {@link
+   * PelTag::DATE_TIME_ORIGINAL}, or {@link
+   * PelTag::DATE_TIME_DIGITIZED}.
+   *
+   * @param int the timestamp held by this entry in the correct form
+   * as indicated by the third argument. For {@link UNIX_TIMESTAMP}
+   * this is an integer counting the number of seconds since January
+   * 1st 1970, for {@link EXIF_STRING} this is a string of the form
+   * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a
+   * floating point number where the integer part denotes the day
+   * count and the fractional part denotes the time of day (0.25 means
+   * 6:00, 0.75 means 18:00).
+   *
+   * @param int the type of the timestamp. This must be one of
+   * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or
+   * {@link JULIAN_DAY_COUNT}.
+   */
+  function __construct($tag, $timestamp, $type = self::UNIX_TIMESTAMP) {
+    parent::__construct($tag);
+    $this->setValue($timestamp, $type);
+  }
+
+  
+  /**
+   * Return the timestamp of the entry.
+   *
+   * The timestamp held by this entry is returned in one of three
+   * formats: as a standard UNIX timestamp (default), as a fractional
+   * Julian Day Count, or as a string.
+   *
+   * @param int the type of the timestamp. This must be one of
+   * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or
+   * {@link JULIAN_DAY_COUNT}.
+   *
+   * @return int the timestamp held by this entry in the correct form
+   * as indicated by the type argument. For {@link UNIX_TIMESTAMP}
+   * this is an integer counting the number of seconds since January
+   * 1st 1970, for {@link EXIF_STRING} this is a string of the form
+   * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a
+   * floating point number where the integer part denotes the day
+   * count and the fractional part denotes the time of day (0.25 means
+   * 6:00, 0.75 means 18:00).
+   */
+  function getValue($type = self::UNIX_TIMESTAMP) {
+    switch ($type) {
+    case self::UNIX_TIMESTAMP:
+      $seconds = $this->convertJdToUnix($this->day_count);
+      if ($seconds === false)
+        /* We get false if the Julian Day Count is outside the range
+         * of a UNIX timestamp. */ 
+        return false;
+      else
+        return $seconds + $this->seconds;
+
+    case self::EXIF_STRING:
+      list($year, $month, $day) = $this->convertJdToGregorian($this->day_count);
+      $hours   = (int)($this->seconds / 3600);
+      $minutes = (int)($this->seconds % 3600 / 60);
+      $seconds = $this->seconds % 60;
+      return sprintf('%04d:%02d:%02d %02d:%02d:%02d',
+                     $year, $month, $day, $hours, $minutes, $seconds);
+    case self::JULIAN_DAY_COUNT:
+      return $this->day_count + $this->seconds / 86400;
+    default:
+      throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' .
+                                            'EXIF_STRING (%d), or ' .
+                                            'JULIAN_DAY_COUNT (%d) for $type, '.
+                                            'got %d.',
+                                            self::UNIX_TIMESTAMP,
+                                            self::EXIF_STRING,
+                                            self::JULIAN_DAY_COUNT,
+                                            $type);
+    }
+  }
+
+
+  /**
+   * Update the timestamp held by this entry.
+   *
+   * @param int the timestamp held by this entry in the correct form
+   * as indicated by the third argument. For {@link UNIX_TIMESTAMP}
+   * this is an integer counting the number of seconds since January
+   * 1st 1970, for {@link EXIF_STRING} this is a string of the form
+   * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a
+   * floating point number where the integer part denotes the day
+   * count and the fractional part denotes the time of day (0.25 means
+   * 6:00, 0.75 means 18:00).
+   *
+   * @param int the type of the timestamp. This must be one of
+   * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or
+   * {@link JULIAN_DAY_COUNT}.
+   */
+  function setValue($timestamp, $type = self::UNIX_TIMESTAMP) {
+    #if (empty($timestamp))
+    #  debug_print_backtrace();
+
+    switch ($type) {
+    case self::UNIX_TIMESTAMP:
+      $this->day_count = $this->convertUnixToJd($timestamp);
+      $this->seconds   = $timestamp % 86400;
+      break;
+
+    case self::EXIF_STRING:
+      /* Clean the timestamp: some timestamps are broken other
+       * separators than ':' and ' '. */
+      $d = preg_split('/[^0-9]+/', $timestamp);
+      $this->day_count = $this->convertGregorianToJd($d[0], $d[1], $d[2]);
+      $this->seconds   = $d[3]*3600 + $d[4]*60 + $d[5];
+      break;
+
+    case self::JULIAN_DAY_COUNT:
+      $this->day_count = (int)floor($timestamp);
+      $this->seconds = (int)(86400 * ($timestamp - floor($timestamp)));
+      break;
+
+    default:
+      throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' .
+                                            'EXIF_STRING (%d), or ' .
+                                            'JULIAN_DAY_COUNT (%d) for $type, '.
+                                            'got %d.',
+                                            self::UNIX_TIMESTAMP,
+                                            self::EXIF_STRING,
+                                            self::JULIAN_DAY_COUNT,
+                                            $type);
+    }
+
+    /* Now finally update the string which will be used when this is
+     * turned into bytes. */
+    parent::setValue($this->getValue(self::EXIF_STRING));
+  }
+
+
+  // The following four functions are used for converting back and
+  // forth between the date formats. They are used in preference to
+  // the ones from the PHP calendar extension to avoid having to
+  // fiddle with timezones and to avoid depending on the extension.
+  //
+  // See http://www.hermetic.ch/cal_stud/jdn.htm#comp for a reference.
+
+  /**
+   * Converts a date in year/month/day format to a Julian Day count.
+   *
+   * @param int $year  the year.
+   * @param int $month the month, 1 to 12.
+   * @param int $day   the day in the month.
+   * @return int the Julian Day count.
+   */
+  function convertGregorianToJd($year, $month, $day) {
+    // Special case mapping 0/0/0 -> 0
+    if ($year == 0 || $month == 0 || $day == 0)
+      return 0;
+
+    $m1412 = ($month <= 2) ? -1 : 0;
+    return floor(( 1461 * ( $year + 4800 + $m1412 ) ) / 4) +
+      floor(( 367 * ( $month - 2 - 12 * $m1412 ) ) / 12) -
+      floor(( 3 * floor( ( $year + 4900 + $m1412 ) / 100 ) ) / 4) +
+      $day - 32075;
+  }
+
+  /**
+   * Converts a Julian Day count to a year/month/day triple.
+   * 
+   * @param int the Julian Day count.
+   * @return array an array with three entries: year, month, day.
+   */
+  function convertJdToGregorian($jd) {
+    // Special case mapping 0 -> 0/0/0
+    if ($jd == 0)
+      return array(0,0,0);
+
+    $l = $jd + 68569;
+    $n = floor(( 4 * $l ) / 146097);
+    $l = $l - floor(( 146097 * $n + 3 ) / 4);
+    $i = floor(( 4000 * ( $l + 1 ) ) / 1461001);
+    $l = $l - floor(( 1461 * $i ) / 4) + 31;
+    $j = floor(( 80 * $l ) / 2447);
+    $d = $l - floor(( 2447 * $j ) / 80);
+    $l = floor($j / 11);
+    $m = $j + 2 - ( 12 * $l );
+    $y = 100 * ( $n - 49 ) + $i + $l;
+    return array($y, $m, $d);
+  }
+
+  /**
+   * Converts a UNIX timestamp to a Julian Day count.
+   *
+   * @param int $timestamp the timestamp.
+   * @return int the Julian Day count.
+   */
+  function convertUnixToJd($timestamp) {
+    return (int)(floor($timestamp / 86400) + 2440588);
+  }
+
+  /**
+   * Converts a Julian Day count to a UNIX timestamp.
+   *
+   * @param int $jd the Julian Day count.
+
+   * @return mixed $timestamp the integer timestamp or false if the
+   * day count cannot be represented as a UNIX timestamp.
+   */
+  function convertJdToUnix($jd) {
+    $timestamp = ($jd - 2440588) * 86400;
+    if ($timestamp != (int)$timestamp)
+      return false;
+    else
+      return $timestamp;
+  }
+
+}
+
+
+/**
+ * Class for holding copyright information.
+ *
+ * The Exif standard specifies a certain format for copyright
+ * information where the one {@link PelTag::COPYRIGHT copyright
+ * tag} holds both the photographer and editor copyrights, separated
+ * by a NULL character.
+ *
+ * This class is used to manipulate that tag so that the format is
+ * kept to the standard.  A common use would be to add a new copyright
+ * tag to an image, since most cameras do not add this tag themselves.
+ * This would be done like this:
+ *
+ * <code>
+ * $entry = new PelEntryCopyright('Copyright, Martin Geisler, 2004');
+ * $ifd0->addEntry($entry);
+ * </code>
+ *
+ * Here we only set the photographer copyright, use the optional
+ * second argument to specify the editor copyright.  If there is only
+ * an editor copyright, then let the first argument be the empty
+ * string.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelEntryCopyright extends PelEntryAscii {
+
+  /**
+   * The photographer copyright.
+   *
+   * @var string
+   */
+  private $photographer;
+
+  /**
+   * The editor copyright.
+   *
+   * @var string
+   */
+  private $editor;
+
+
+  /**
+   * Make a new entry for holding copyright information.
+   *
+   * @param string the photographer copyright.  Use the empty string
+   * if there is no photographer copyright.
+   *
+   * @param string the editor copyright.  Use the empty string if
+   * there is no editor copyright.
+   */
+  function __construct($photographer = '', $editor = '') {
+    parent::__construct(PelTag::COPYRIGHT);
+    $this->setValue($photographer, $editor);
+  }
+  
+
+  /**
+   * Update the copyright information.
+   *
+   * @param string the photographer copyright.  Use the empty string
+   * if there is no photographer copyright.
+   *
+   * @param string the editor copyright.  Use the empty string if
+   * there is no editor copyright.
+   */
+  function setValue($photographer = '', $editor = '') {
+    $this->photographer = $photographer;
+    $this->editor       = $editor;
+
+    if ($photographer == '' && $editor != '')
+      $photographer = ' ';
+
+    if ($editor == '')
+      parent::setValue($photographer);
+    else
+      parent::setValue($photographer . chr(0x00) . $editor);
+  }
+
+
+  /**
+   * Retrive the copyright information.
+   *
+   * The strings returned will be the same as the one used previously
+   * with either {@link __construct the constructor} or with {@link
+   * setValue}.
+   *
+   * @return array an array with two strings, the photographer and
+   * editor copyrights.  The two fields will be returned in that
+   * order, so that the first array index will be the photographer
+   * copyright, and the second will be the editor copyright.
+   */
+  function getValue() {
+    return array($this->photographer, $this->editor);
+  }
+
+
+  /**
+   * Return a text string with the copyright information.
+   *
+   * The photographer and editor copyright fields will be returned
+   * with a '-' in between if both copyright fields are present,
+   * otherwise only one of them will be returned.
+   *
+   * @param boolean if false, then the strings '(Photographer)' and
+   * '(Editor)' will be appended to the photographer and editor
+   * copyright fields (if present), otherwise the fields will be
+   * returned as is.
+   *
+   * @return string the copyright information in a string.
+   */
+  function getText($brief = false) {
+    if ($brief) {
+      $p = '';
+      $e = '';
+    } else {
+      $p = ' ' . Pel::tra('(Photographer)');
+      $e = ' ' . Pel::tra('(Editor)');
+    }
+
+    if ($this->photographer != '' && $this->editor != '')
+      return $this->photographer . $p . ' - ' . $this->editor . $e;
+    
+    if ($this->photographer != '')
+      return $this->photographer . $p;
+
+    if ($this->editor != '')
+      return $this->editor . $e;
+
+    return '';
+  }
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelJpegMarker.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelJpegMarker.php
--- ./PEL/PelJpegMarker.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelJpegMarker.php	2011/09/04 21:16:14
@@ -0,0 +1,435 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005, 2006  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelJpegMarker.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Classes for dealing with JPEG markers.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**#@+ Required class definitions. */
+require_once('Pel.php');
+/**#@-*/
+
+
+/**
+ * Class with static methods for JPEG markers.
+ *
+ * This class defines the constants to be used whenever one refers to
+ * a JPEG marker.  All the methods defined are static, and they all
+ * operate on one argument which should be one of the class constants.
+ * They will all be denoted by PelJpegMarker in the documentation.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelJpegMarker {
+
+  /** Encoding (baseline) */
+  const SOF0  = 0xC0;
+  /** Encoding (extended sequential) */
+  const SOF1  = 0xC1;
+  /** Encoding (progressive) */
+  const SOF2  = 0xC2;
+  /** Encoding (lossless) */
+  const SOF3  = 0xC3;
+  /** Define Huffman table */
+  const DHT   = 0xC4;
+  /** Encoding (differential sequential) */
+  const SOF5  = 0xC5;
+  /** Encoding (differential progressive) */
+  const SOF6  = 0xC6;
+  /** Encoding (differential lossless) */
+  const SOF7  = 0xC7;
+  /** Extension */
+  const JPG   = 0xC8;
+  /** Encoding (extended sequential, arithmetic) */
+  const SOF9  = 0xC9;
+  /** Encoding (progressive, arithmetic) */
+  const SOF10 = 0xCA;
+  /** Encoding (lossless, arithmetic) */
+  const SOF11 = 0xCB;
+  /** Define arithmetic coding conditioning */
+  const DAC   = 0xCC;
+  /** Encoding (differential sequential, arithmetic) */
+  const SOF13 = 0xCD;
+  /** Encoding (differential progressive, arithmetic) */
+  const SOF14 = 0xCE;
+  /** Encoding (differential lossless, arithmetic) */
+  const SOF15 = 0xCF;
+  /** Restart 0 */
+  const RST0  = 0xD0;
+  /** Restart 1 */
+  const RST1  = 0xD1;
+  /** Restart 2 */
+  const RST2  = 0xD2;
+  /** Restart 3 */
+  const RST3  = 0xD3;
+  /** Restart 4 */
+  const RST4  = 0xD4;
+  /** Restart 5 */
+  const RST5  = 0xD5;
+  /** Restart 6 */
+  const RST6  = 0xD6;
+  /** Restart 7 */
+  const RST7  = 0xD7;
+  /** Start of image */
+  const SOI   = 0xD8;
+  /** End of image */
+  const EOI   = 0xD9;
+  /** Start of scan */
+  const SOS   = 0xDA;
+  /** Define quantization table */
+  const DQT   = 0xDB;
+  /** Define number of lines */
+  const DNL   = 0xDC;
+  /** Define restart interval */
+  const DRI   = 0xDD;
+  /** Define hierarchical progression */
+  const DHP   = 0xDE;
+  /** Expand reference component */
+  const EXP   = 0xDF;
+  /** Application segment 0 */
+  const APP0  = 0xE0;
+  /**
+   * Application segment 1
+   *
+   * When a JPEG image contains Exif data, the data will normally be
+   * stored in this section and a call to {@link PelJpeg::getExif()}
+   * will return a {@link PelExif} object representing it.
+   */
+  const APP1  = 0xE1;
+  /** Application segment 2 */
+  const APP2  = 0xE2;
+  /** Application segment 3 */
+  const APP3  = 0xE3;
+  /** Application segment 4 */
+  const APP4  = 0xE4;
+  /** Application segment 5 */
+  const APP5  = 0xE5;
+  /** Application segment 6 */
+  const APP6  = 0xE6;
+  /** Application segment 7 */
+  const APP7  = 0xE7;
+  /** Application segment 8 */
+  const APP8  = 0xE8;
+  /** Application segment 9 */
+  const APP9  = 0xE9;
+  /** Application segment 10 */
+  const APP10 = 0xEA;
+  /** Application segment 11 */
+  const APP11 = 0xEB;
+  /** Application segment 12 */
+  const APP12 = 0xEC;
+  /** Application segment 13 */
+  const APP13 = 0xED;
+  /** Application segment 14 */
+  const APP14 = 0xEE;
+  /** Application segment 15 */
+  const APP15 = 0xEF;
+  /** Extension 0 */
+  const JPG0  = 0xF0;
+  /** Extension 1 */
+  const JPG1  = 0xF1;
+  /** Extension 2 */
+  const JPG2  = 0xF2;
+  /** Extension 3 */
+  const JPG3  = 0xF3;
+  /** Extension 4 */
+  const JPG4  = 0xF4;
+  /** Extension 5 */
+  const JPG5  = 0xF5;
+  /** Extension 6 */
+  const JPG6  = 0xF6;
+  /** Extension 7 */
+  const JPG7  = 0xF7;
+  /** Extension 8 */
+  const JPG8  = 0xF8;
+  /** Extension 9 */
+  const JPG9  = 0xF9;
+  /** Extension 10 */
+  const JPG10 = 0xFA;
+  /** Extension 11 */
+  const JPG11 = 0xFB;
+  /** Extension 12 */
+  const JPG12 = 0xFC;
+  /** Extension 13 */
+  const JPG13 = 0xFD;
+  /** Comment */
+  const COM   = 0xFE;
+
+  /**
+   * Check if a byte is a valid JPEG marker.
+   *
+   * @param PelJpegMarker the byte that will be checked.
+   *
+   * @return boolean if the byte is recognized true is returned,
+   * otherwise false will be returned.
+   */
+  static function isValid($m) {
+    return ($m >= self::SOF0 && $m <= self::COM);
+  }
+  
+  /**
+   * Turn a JPEG marker into bytes.
+   *
+   * @param PelJpegMarker the marker.
+   *
+   * @return string the marker as a string.  This will be a string
+   * with just a single byte since all JPEG markers are simply single
+   * bytes.
+   */
+  static function getBytes($m) {
+    return chr($m);
+  }
+
+  /**
+   * Return the short name for a marker.
+   *
+   * @param PelJpegMarker the marker.
+   *
+   * @return string the name of the marker, e.g., 'SOI' for the Start
+   * of Image marker.
+   */
+  static function getName($m) {
+    switch ($m) {
+    case self::SOF0:  return 'SOF0';
+    case self::SOF1:  return 'SOF1';
+    case self::SOF2:  return 'SOF2';
+    case self::SOF3:  return 'SOF3';
+    case self::SOF5:  return 'SOF5';
+    case self::SOF6:  return 'SOF6';
+    case self::SOF7:  return 'SOF7';
+    case self::SOF9:  return 'SOF9';
+    case self::SOF10: return 'SOF10';
+    case self::SOF11: return 'SOF11';
+    case self::SOF13: return 'SOF13';
+    case self::SOF14: return 'SOF14';
+    case self::SOF15: return 'SOF15';
+    case self::SOI:   return 'SOI';
+    case self::EOI:   return 'EOI';
+    case self::SOS:   return 'SOS';
+    case self::COM:   return 'COM';
+    case self::DHT:   return 'DHT';
+    case self::JPG:   return 'JPG';
+    case self::DAC:   return 'DAC';
+    case self::RST0:  return 'RST0';
+    case self::RST1:  return 'RST1';
+    case self::RST2:  return 'RST2';
+    case self::RST3:  return 'RST3';
+    case self::RST4:  return 'RST4';
+    case self::RST5:  return 'RST5';
+    case self::RST6:  return 'RST6';
+    case self::RST7:  return 'RST7';
+    case self::DQT:   return 'DQT';
+    case self::DNL:   return 'DNL';
+    case self::DRI:   return 'DRI';
+    case self::DHP:   return 'DHP';
+    case self::EXP:   return 'EXP';
+    case self::APP0:  return 'APP0';
+    case self::APP1:  return 'APP1';
+    case self::APP2:  return 'APP2';
+    case self::APP3:  return 'APP3';
+    case self::APP4:  return 'APP4';
+    case self::APP5:  return 'APP5';
+    case self::APP6:  return 'APP6';
+    case self::APP7:  return 'APP7';
+    case self::APP8:  return 'APP8';
+    case self::APP9:  return 'APP9';
+    case self::APP10: return 'APP10';
+    case self::APP11: return 'APP11';
+    case self::APP12: return 'APP12';
+    case self::APP13: return 'APP13';
+    case self::APP14: return 'APP14';
+    case self::APP15: return 'APP15';
+    case self::JPG0:  return 'JPG0';
+    case self::JPG1:  return 'JPG1';
+    case self::JPG2:  return 'JPG2';
+    case self::JPG3:  return 'JPG3';
+    case self::JPG4:  return 'JPG4';
+    case self::JPG5:  return 'JPG5';
+    case self::JPG6:  return 'JPG6';
+    case self::JPG7:  return 'JPG7';
+    case self::JPG8:  return 'JPG8';
+    case self::JPG9:  return 'JPG9';
+    case self::JPG10: return 'JPG10';
+    case self::JPG11: return 'JPG11';
+    case self::JPG12: return 'JPG12';
+    case self::JPG13: return 'JPG13';
+    case self::COM:   return 'COM';
+    default:          return Pel::fmt('Unknown marker: 0x%02X', $m);
+    }
+  }
+
+  /**
+   * Returns a description of a JPEG marker.
+   *
+   * @param PelJpegMarker the marker.
+   *
+   * @return string the description of the marker.
+   */
+  static function getDescription($m) {
+    switch ($m) {
+    case self::SOF0:
+      return Pel::tra('Encoding (baseline)');
+    case self::SOF1:
+      return Pel::tra('Encoding (extended sequential)');
+    case self::SOF2:
+      return Pel::tra('Encoding (progressive)');
+    case self::SOF3:
+      return Pel::tra('Encoding (lossless)');
+    case self::SOF5:
+      return Pel::tra('Encoding (differential sequential)');
+    case self::SOF6:
+      return Pel::tra('Encoding (differential progressive)');
+    case self::SOF7:
+      return Pel::tra('Encoding (differential lossless)');
+    case self::SOF9:
+      return Pel::tra('Encoding (extended sequential, arithmetic)');
+    case self::SOF10:
+      return Pel::tra('Encoding (progressive, arithmetic)');
+    case self::SOF11:
+      return Pel::tra('Encoding (lossless, arithmetic)');
+    case self::SOF13:
+      return Pel::tra('Encoding (differential sequential, arithmetic)');
+    case self::SOF14:
+      return Pel::tra('Encoding (differential progressive, arithmetic)');
+    case self::SOF15:
+      return Pel::tra('Encoding (differential lossless, arithmetic)');
+    case self::SOI:
+      return Pel::tra('Start of image');
+    case self::EOI:
+      return Pel::tra('End of image');
+    case self::SOS:
+      return Pel::tra('Start of scan');
+    case self::COM:
+      return Pel::tra('Comment');
+    case self::DHT:
+      return Pel::tra('Define Huffman table');
+    case self::JPG:
+      return Pel::tra('Extension');
+    case self::DAC:
+      return Pel::tra('Define arithmetic coding conditioning');
+    case self::RST0:
+      return Pel::fmt('Restart %d', 0);
+    case self::RST1:
+      return Pel::fmt('Restart %d', 1);
+    case self::RST2:
+      return Pel::fmt('Restart %d', 2);
+    case self::RST3:
+      return Pel::fmt('Restart %d', 3);
+    case self::RST4:
+      return Pel::fmt('Restart %d', 4);
+    case self::RST5:
+      return Pel::fmt('Restart %d', 5);
+    case self::RST6:
+      return Pel::fmt('Restart %d', 6);
+    case self::RST7:
+      return Pel::fmt('Restart %d', 7);
+    case self::DQT:
+      return Pel::tra('Define quantization table');
+    case self::DNL:
+      return Pel::tra('Define number of lines');
+    case self::DRI:
+      return Pel::tra('Define restart interval');
+    case self::DHP:
+      return Pel::tra('Define hierarchical progression');
+    case self::EXP:
+      return Pel::tra('Expand reference component');
+    case self::APP0:
+      return Pel::fmt('Application segment %d', 0);
+    case self::APP1:
+      return Pel::fmt('Application segment %d', 1);
+    case self::APP2:
+      return Pel::fmt('Application segment %d', 2);
+    case self::APP3:
+      return Pel::fmt('Application segment %d', 3);
+    case self::APP4:
+      return Pel::fmt('Application segment %d', 4);
+    case self::APP5:
+      return Pel::fmt('Application segment %d', 5);
+    case self::APP6:
+      return Pel::fmt('Application segment %d', 6);
+    case self::APP7:
+      return Pel::fmt('Application segment %d', 7);
+    case self::APP8:
+      return Pel::fmt('Application segment %d', 8);
+    case self::APP9:
+      return Pel::fmt('Application segment %d', 9);
+    case self::APP10:
+      return Pel::fmt('Application segment %d', 10);
+    case self::APP11:
+      return Pel::fmt('Application segment %d', 11);
+    case self::APP12:
+      return Pel::fmt('Application segment %d', 12);
+    case self::APP13:
+      return Pel::fmt('Application segment %d', 13);
+    case self::APP14:
+      return Pel::fmt('Application segment %d', 14);
+    case self::APP15:
+      return Pel::fmt('Application segment %d', 15);
+    case self::JPG0:
+      return Pel::fmt('Extension %d', 0);
+    case self::JPG1:
+      return Pel::fmt('Extension %d', 1);
+    case self::JPG2:
+      return Pel::fmt('Extension %d', 2);
+    case self::JPG3:
+      return Pel::fmt('Extension %d', 3);
+    case self::JPG4:
+      return Pel::fmt('Extension %d', 4);
+    case self::JPG5:
+      return Pel::fmt('Extension %d', 5);
+    case self::JPG6:
+      return Pel::fmt('Extension %d', 6);
+    case self::JPG7:
+      return Pel::fmt('Extension %d', 7);
+    case self::JPG8:
+      return Pel::fmt('Extension %d', 8);
+    case self::JPG9:
+      return Pel::fmt('Extension %d', 9);
+    case self::JPG10:
+      return Pel::fmt('Extension %d', 10);
+    case self::JPG11:
+      return Pel::fmt('Extension %d', 11);
+    case self::JPG12:
+      return Pel::fmt('Extension %d', 12);
+    case self::JPG13:
+      return Pel::fmt('Extension %d', 13);
+    case self::COM:
+      return Pel::tra('Comment');
+    default:
+      return Pel::fmt('Unknown marker: 0x%02X', $m);
+    }
+  }
+}
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PEL/RCS/PelFormat.php,v
retrieving revision 1.1
diff -u -r1.1 ./PEL/PelFormat.php
--- ./PEL/PelFormat.php	2011/06/22 21:16:17	1.1
+++ ./PEL/PelFormat.php	2011/09/04 21:16:14
@@ -0,0 +1,225 @@
+<?php
+
+/*  PEL: PHP Exif Library.  A library with support for reading and
+ *  writing all Exif headers in JPEG and TIFF images using PHP.
+ *
+ *  Copyright (C) 2004, 2005  Martin Geisler.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program in the file COPYING; if not, write to the
+ *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+/* $Id: PelFormat.php,v 1.2 2011/06/22 21:18:03 root Exp $ */
+
+
+/**
+ * Namespace for functions operating on Exif formats.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @version $Revision: 1.2 $
+ * @date $Date: 2011/06/22 21:18:03 $
+ * @license http://www.gnu.org/licenses/gpl.html GNU General Public
+ * License (GPL)
+ * @package PEL
+ */
+
+/**
+ * Namespace for functions operating on Exif formats.
+ *
+ * This class defines the constants that are to be used whenever one
+ * has to refer to the format of an Exif tag.  They will be
+ * collectively denoted by the pseudo-type PelFormat throughout the
+ * documentation.
+ *
+ * All the methods defined here are static, and they all operate on a
+ * single argument which should be one of the class constants.
+ *
+ * @author Martin Geisler <mgeisler@users.sourceforge.net>
+ * @package PEL
+ */
+class PelFormat {
+
+  /**
+   * Unsigned byte.
+   *
+   * Each component will be an unsigned 8-bit integer with a value
+   * between 0 and 255.
+   *
+   * Modelled with the {@link PelEntryByte} class.
+   */
+  const BYTE       =  1;
+  
+  /**
+   * ASCII string.
+   *
+   * Each component will be an ASCII character.
+   *
+   * Modelled with the {@link PelEntryAscii} class.
+   */
+  const ASCII      =  2;
+  
+  /**
+   * Unsigned short.
+   *
+   * Each component will be an unsigned 16-bit integer with a value
+   * between 0 and 65535.
+   *
+   * Modelled with the {@link PelEntryShort} class.
+   */
+  const SHORT      =  3;
+
+  /**
+   * Unsigned long.
+   *
+   * Each component will be an unsigned 32-bit integer with a value
+   * between 0 and 4294967295.
+   *
+   * Modelled with the {@link PelEntryLong} class.
+   */
+  const LONG       =  4;
+
+  /**
+   * Unsigned rational number.
+   *
+   * Each component will consist of two unsigned 32-bit integers
+   * denoting the enumerator and denominator.  Each integer will have
+   * a value between 0 and 4294967295.
+   *
+   * Modelled with the {@link PelEntryRational} class.
+   */
+  const RATIONAL   =  5;
+
+  /**
+   * Signed byte.
+   *
+   * Each component will be a signed 8-bit integer with a value
+   * between -128 and 127.
+   *
+   * Modelled with the {@link PelEntrySByte} class.
+   */
+  const SBYTE      =  6;
+
+  /**
+   * Undefined byte.
+   *
+   * Each component will be a byte with no associated interpretation.
+   *
+   * Modelled with the {@link PelEntryUndefined} class.
+   */
+  const UNDEFINED  =  7;
+
+  /**
+   * Signed short.
+   *
+   * Each component will be a signed 16-bit integer with a value
+   * between -32768 and 32767.
+   *
+   * Modelled with the {@link PelEntrySShort} class.
+   */
+  const SSHORT     =  8;
+
+  /**
+   * Signed long.
+   *
+   * Each component will be a signed 32-bit integer with a value
+   * between -2147483648 and 2147483647.
+   *
+   * Modelled with the {@link PelEntrySLong} class.
+   */
+  const SLONG      =  9;
+
+  /**
+   * Signed rational number.
+   *
+   * Each component will consist of two signed 32-bit integers
+   * denoting the enumerator and denominator.  Each integer will have
+   * a value between -2147483648 and 2147483647.
+   *
+   * Modelled with the {@link PelEntrySRational} class.
+   */
+  const SRATIONAL  = 10;
+
+  /**
+   * Floating point number.
+   *
+   * Entries with this format are not currently implemented.
+   */
+  const FLOAT      = 11;
+
+  /**
+   * Double precision floating point number.
+   *
+   * Entries with this format are not currently implemented.
+   */
+  const DOUBLE     = 12;
+
+
+  /**
+   * Returns the name of a format.
+   *
+   * @param PelFormat the format.
+   *
+   * @return string the name of the format, e.g., 'Ascii' for the
+   * {@link ASCII} format etc.
+   */
+  static function getName($type) {
+    switch ($type) {
+    case self::ASCII:     return 'Ascii';
+    case self::BYTE:      return 'Byte';
+    case self::SHORT:     return 'Short';
+    case self::LONG:      return 'Long';
+    case self::RATIONAL:  return 'Rational';
+    case self::SBYTE:     return 'SByte';
+    case self::SSHORT:    return 'SShort';
+    case self::SLONG:     return 'SLong';
+    case self::SRATIONAL: return 'SRational';
+    case self::FLOAT:     return 'Float';
+    case self::DOUBLE:    return 'Double';
+    case self::UNDEFINED: return 'Undefined';
+    default:
+      return Pel::fmt('Unknown format: 0x%X', $type);
+    }
+  }
+
+
+  /**
+   * Return the size of components in a given format.
+   *
+   * @param PelFormat the format.
+   *
+   * @return the size in bytes needed to store one component with the
+   * given format.
+   */
+  static function getSize($type) {
+    switch ($type) {
+    case self::ASCII:     return 1;
+    case self::BYTE:      return 1;
+    case self::SHORT:     return 2;
+    case self::LONG:      return 4;
+    case self::RATIONAL:  return 8;
+    case self::SBYTE:     return 1;
+    case self::SSHORT:    return 2;
+    case self::SLONG:     return 4;
+    case self::SRATIONAL: return 8;
+    case self::FLOAT:     return 4;
+    case self::DOUBLE:    return 8;
+    case self::UNDEFINED: return 1;
+    default:
+      return Pel::fmt('Unknown format: 0x%X', $type);
+    }
+  }
+
+}
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./themes/default/templates/RCS/browse.tpl,v
retrieving revision 1.1
diff -u -r1.1 ./themes/default/templates/browse.tpl
--- ./themes/default/templates/browse.tpl	2011/07/01 19:25:45	1.1
+++ ./themes/default/templates/browse.tpl	2011/07/01 21:47:59
@@ -172,4 +172,10 @@
     <% /foreach %>
 </table>
 <div id="pagenav"><% $pager %></div>
+<div id="gps">
+<% $navmap %>
+<a href="javascript:initMap();switch_display('navmap');switch_display('map_zoom');">&Uuml;bersichtskarte an/aus</a>
+</div>
+<div id="map_zoom" style="display:none;"> <a href="javascript:upsize_display('navmap');map.fitBounds(latlngbounds);">+++</a>/<a href="javascript:downsize_display('navmap');map.fitBounds(latlngbounds);">---</a></div>
+<div id="navmap" style="display:none; height:240px;width:100%;"></div>
 
===================================================================
RCS file: ./themes/default/templates/RCS/display.tpl,v
retrieving revision 1.1
diff -u -r1.1 ./themes/default/templates/display.tpl
--- ./themes/default/templates/display.tpl	2010/09/24 19:52:14	1.1
+++ ./themes/default/templates/display.tpl	2011/09/04 21:16:04
@@ -29,6 +29,8 @@
         $picture[metadata_found]    - Set to 1 if the picture does contains IPTC metadata
         $picture[mime]              - MIME type (if listed in filetypes.inc.php) - Used for videos
         $picture[player]            - Recommended player (used along with video)
+        $picture[latitude]
+        $picture[longitude]
         $rating                     - An array dedicated to the rating module
 
 *%>
@@ -177,7 +179,8 @@
         <img src="base/images/unknown.gif" alt="<% $picture[title] %>" class="icon" />
     <% /if %>
     <% if $picture[link] %></a><% /if %>
-    
+
+
     <% if $config[use_exif] && $picture[formatted_exif_metadata] %>
         <div class="exifmetadata">
         <% $picture[formatted_exif_metadata] %>
@@ -223,4 +226,83 @@
     </div><!--//comments-->
     <% /if %>
 
+    <% if $config[use_gps] %>
+    <div id="gps">
+      <% if $picture[latitude] != 'unknown' && $picture[longitude] != 'unknown' %>
+      Location: <% $picture[latitude] %> <% $picture[longitude] %><br />
+      <% /if %>
+      <div style="text-align: center">
+        <% if $admin %>
+          <% if $picture[latitude] != 'unknown' && $picture[longitude] != 'unknown' %>
+            <a href="javascript:map_initialize(<% $picture[latitude_dec] %>,<% $picture[longitude_dec] %>,true,16,false);switch_display('map_canvas');switch_display('map_zoom');switch_display('infoPanel');switch_display('editPanel1');switch_display('editPanel2');"><% $txt[toggle_map] %></a>
+          <% else %>
+            <a href="javascript:map_initialize(46.6,14.3,true,10,true);switch_display('map_canvas');switch_display('map_zoom');switch_display('infoPanel');switch_display('editPanel1');switch_display('editPanel2');"><% $txt[toggle_map] %></a>
+          <% /if %>
+        <% else %>
+          <% if $picture[latitude] != 'unknown' && $picture[longitude] != 'unknown' %>
+    <a href="javascript:map_initialize(<% $picture[latitude_dec] %>,<% $picture[longitude_dec] %>,false,16,false);switch_display('map_canvas');switch_display('map_zoom');switch_display('infoPanel');switch_display('editPanel1');switch_display('editPanel2');"><% $txt[toggle_map] %></a>
+          <% /if %>
+        <% /if %>
+      </div>
+
+
+   <div id="map_zoom" style="display:none;"> <a href="javascript:upsize_display('map_canvas')">+++</a>/<a href="javascript:downsize_display('map_canvas')">---</a></div>
+ <div id="map_canvas" style="display:none; height:240px;width:100%;"></div>
+  <% $gps_form %>
+
+<% if $admin %>
+<div id="gpsEdit" style="display:inline-block;">
+  <div id="editPanel1" style="display:none;float:left;clear:right;">
+  <!-- input type="hidden" name="display" value="<% $display %>" / -->
+   <div id="lastpos">
+     <% $lastpicthumb %>
+     <br />
+     <a href="javascript:marker.setPosition(new google.maps.LatLng( <% $last_picture_latitude_dec %>, <% $last_picture_longitude_dec %>));map.setCenter(new google.maps.LatLng( <% $last_picture_latitude_dec %>, <% $last_picture_longitude_dec %>));document.getElementById('lat').value = <% $last_picture_latitude_dec %>; document.getElementById('lon').value =<% $last_picture_longitude_dec %> ;geocodePosition(marker.getPosition());map.setZoom(16);void(0);">take last position</a>
+  </div>
+  </div>
+<% /if %>
+  <div id="infoPanel" style="display:none;float:left;clear:right;">
+<% if $admin %>
+    <b>Marker status:</b>
+    <div id="markerStatus"><i>Click and drag the marker.</i></div>
+<% /if %>
+    <b>Current position:</b>
+<% if $admin %>
+    <div id="position_info">
+       <input type="text" id="lat" name="lat" size="16">
+<input type="text" id="lon" name="lon" size="16">
+    </div>
+<% else %>
+    <div id="position_info"></div>
+<% /if %>
+    <b>Closest matching address:</b>
+<% if $admin %>
+    <div id="address"><input type="text" size="32" name="address_txt" value="... wait" id="address_txt" x-webkit-speech="x-webkit-speech"/> 
+   <a href="javascript:showLocation();">search</a><br/>
+  <input type="submit" value="Location Update" name="pos_update"/>
+   </div>
+<% else %>
+    <div id="address"></div>
+<% /if %>
+  </div> <!-- end info panel-->
+
+<% if $admin %>
+  <div id="editPanel2" style="display:none;float:left;clear:right;">
+  <!-- input type="hidden" name="display" value="<% $display %>" / -->
+   <div id="nextpos">
+     <% $nextpicthumb %>
+     <br />
+     <a href="javascript:marker.setPosition(new google.maps.LatLng( <% $next_picture_latitude_dec %>, <% $next_picture_longitude_dec %>));map.setCenter(new google.maps.LatLng( <% $next_picture_latitude_dec %>, <% $next_picture_longitude_dec %>));document.getElementById('lat').value = <% $next_picture_latitude_dec %>; document.getElementById('lon').value =<% $next_picture_longitude_dec %> ;geocodePosition(marker.getPosition());map.setZoom(16);void(0);">take next position</a>
+  </div>
+  </div>
+  </div>
+<% /if %>
+  </form>
+ 
+
+    </div><!--//gpsdiv-->
+    <% /if %> <!-- gps -->
+
+
+
 </div><!--//displaypicture-->
===================================================================
RCS file: ./themes/default/RCS/header.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./themes/default/header.inc.php
--- ./themes/default/header.inc.php	2010/09/25 07:31:03	1.1
+++ ./themes/default/header.inc.php	2011/08/27 08:02:00
@@ -36,6 +36,15 @@
     <?php endif; ?>
     <link rel="alternate" type="application/rss+xml" title="RSS 1.0" href="<?php echo SCRIPT_NAME; ?>?lastaddedpictures=&amp;rss=1" />
     <link rel="alternate" type="application/rss+xml" title="RSS 1.0" href="<?php echo SCRIPT_NAME; ?>?lastaddedpicturesperdir=&amp;rss=1" />
+    
+<script type="text/javascript"
+<?php
+    if ($_SERVER['HTTPS'] == on) $proto = 'https'; else $proto='http';
+    print("src=\"".$proto."://maps.google.com/maps/api/js?sensor=false\">");
+?>
+</script>
+<script src="<?php echo $base_js_dir ?>draggable-markers.js" type="text/javascript"></script>
+
 
 <?php
     /* Only used during installation */
@@ -50,7 +59,7 @@
 ?>
     <title><?php echo $txt_site_title ?></title>
   </head>
-<body <?php if($mode == 'slideshow') echo 'onload="slides.update()"'?>>
+<body <?php if($mode == 'slideshow') echo 'onload="slides.update()"'?> >
 <div <?php if (!$_GET['popup']) echo 'id="main"'; ?>>
 
 <?php if($mode != 'slideshow') { ?>
===================================================================
RCS file: ./base/include/RCS/default-config.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/include/default-config.inc.php
--- ./base/include/default-config.inc.php	2010/09/24 19:56:22	1.1
+++ ./base/include/default-config.inc.php	2011/09/04 21:16:41
@@ -17,7 +17,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: default-config.inc.php,v 1.1 2010/09/24 19:56:22 root Exp $
+*  $Id: default-config.inc.php,v 1.2 2010/09/25 22:32:44 root Exp $
 *
 */
 
@@ -423,6 +423,14 @@
 $config['use_comments'] = 1;
 
 /***
+* directive: use_gps
+* type: boolean
+* category: modules/gps
+* description: Enable/Disable the use of the Coordnates
+***/
+$config['use_gps'] = 1;
+
+/***
 * directive: postcomment_min_level
 * type: int
 * available_from: 0.9.11
===================================================================
RCS file: ./base/include/RCS/functions_global.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/include/functions_global.inc.php
--- ./base/include/functions_global.inc.php	2010/09/17 17:11:16	1.1
+++ ./base/include/functions_global.inc.php	2011/09/04 21:16:41
@@ -17,7 +17,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: functions_global.inc.php,v 1.1 2010/09/17 17:11:16 root Exp $
+*  $Id: functions_global.inc.php,v 1.2 2010/09/25 22:32:44 root Exp $
 *
 */
 
@@ -725,11 +725,41 @@
     // Get mtime for every entry
     foreach($files as $file) {
         $fullpath = $current_dir.$file;
-        $temp_array[$file]=filemtime($fullpath);
+        $exif = get_exif_data($fullpath);
+        if($exif['Exif.DateTime']){
+#echo "<pre>";
+#print_r($exif['Exif.DateTime']);
+#echo "</pre>";
+          preg_match("/^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/", $exif['Exif.DateTime'], $result);
+          $D=$result[3];
+          $M=$result[2];
+          $Y=$result[1];
+          $h=$result[4];
+          $m=$result[5];
+          $s=$result[6];
+#echo "<pre>
+#result:";
+#print_r($result);
+#echo "</pre>";
+          $res_time=mktime($h,$m,$s,$M,$D,$Y);
+#echo "<pre>
+#result time:";
+#print_r($res_time);
+#echo "</pre>";
+          $temp_array[$file]=$res_time;
+        } else
+      $temp_array[$file]=filemtime($fullpath);
     }
 
     // Sort entries
+#echo "<pre>";
+#print_r($temp_array);
+#echo "</pre>";
+
     asort($temp_array);
+#echo "<pre>";
+#print_r($temp_array);
+#echo "</pre>";
 
     // Rebuild a simple array
     foreach($temp_array as $file => $filemtime) {
===================================================================
RCS file: ./base/include/RCS/functions_metadata.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/include/functions_metadata.inc.php
--- ./base/include/functions_metadata.inc.php	2010/09/17 18:15:30	1.1
+++ ./base/include/functions_metadata.inc.php	2011/09/04 21:16:41
@@ -16,7 +16,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-* $Id: functions_metadata.inc.php,v 1.1 2010/09/17 18:15:30 root Exp $
+* $Id: functions_metadata.inc.php,v 1.9 2011/08/17 20:17:26 root Exp $
 *
 */
 
@@ -44,7 +44,10 @@
 		'Exif.DateTime', 		        // Picture DateTime (YYYY-MM-DD HH:II:SS)
 		'Exif.UserComment',             // Picture's UserComment
 		'Exif.ImageDescription',        // Picture's Description/Title
-		'Exif.JpegComment'              // Jpeg Comment (Which is not truly EXIF but can be read by the function)
+		'Exif.JpegComment',              // Jpeg Comment (Which is not truly EXIF but can be read by the function)
+		'Exif.Orientation',               // Orientation
+		'Exif.Latitude',                 // GPS Latitude
+		'Exif.Longitude'                 // GPS Longitude
 
 		);
 
@@ -62,8 +65,12 @@
     //trigger_error('The exif related functions are not available on this server, disable the use of exif in the configuration', ERROR);
     return false;
 }
+if(is_file($image_path))
+  $exif_header=@exif_read_data($image_path,'EXIF');
 
-$exif_header=@exif_read_data($image_path,'EXIF');
+#echo "<pre>";
+#print_r($exif_header);
+#echo "</pre>";
 
 if (!is_array($exif_header)) return false;
 
@@ -105,14 +112,22 @@
 
 if ($exif_header['ExposureTime']) {
    list($div,$ExposureTime)=split("/", $exif_header['ExposureTime']);
+   if($ExposureTime==0 || $div==0)
+     $ExposureTime="1/?";
+   else{
    $ExposureTime=$div/$ExposureTime;
    if ($ExposureTime < 1) $ExposureTime="1/".round(1/$ExposureTime,2);
    }
+   }
 elseif ($exif_header['ShutterSpeedValue']) {
    list($ExposureTime,$div)=split("/", $exif_header['ShutterSpeedValue']);
+   if($ExposureTime==0 || $div==0)
+    $ExposureTime="1/?";
+   else{
    $ExposureTime=($div/10)/$ExposureTime;
    if ($ExposureTime < 1) $ExposureTime="1/".round(1/$ExposureTime,2);
    }
+   }
 elseif ($exif_header["COMPUTED"]["ExposureTime"]) {
 	ereg(".*\((.*)\)",$exif_header["COMPUTED"]["ExposureTime"], $res);
 	$WorkingExposureTime=$res[1];
@@ -131,12 +146,34 @@
 if ($FNumber) $result['Exif.FNumber']=$FNumber;
 
 
+
+// Getting and Formatting Exif.Orientation
+if ($exif_header["Orientation"]) {
+   switch ($exif_header["Orientation"]) {
+     case 1:
+      $Orientation='normal';
+      break;
+     case 6:
+      $Orientation='rotate cw 90';
+      break;
+     case 8:
+      $Orientation='rotate cw 270';
+      break;
+     case 16:
+      $Orientation='rotate 180';
+      break;
+     default:
+      $Orientation=$exif_header["Orientation"];
+      break;
+   }
+if ($Orientation) $result['Exif.Orientation']=$Orientation;
+} else $result['Exif.Orientation']='unknown';
+
 // Getting and Formatting Exif.FocalLength
 if ($exif_header["FocalLength"]) {
    list($FocalLength,$div)=split("/", $exif_header["FocalLength"]);
    $FocalLength=round($FocalLength/$div,2);
 if ($FocalLength) $result['Exif.FocalLength']=$FocalLength;
-
 // Try to get or calculate Exif.FocalLengthIn35mmFilm (35mm film equivalent of FocalLength)
 // For the calcul, the model need to be registered in sensors.dat
 if ($exif_header['FocalLengthIn35mmFilm']) $result['Exif.FocalLengthIn35mmFilm']=$exif_header['FocalLengthIn35mmFilm'];
@@ -184,8 +221,42 @@
 // Getting and Formatting Exif.DateTime 
 if ($exif_header['DateTimeOriginal'])  {
    $result['Exif.DateTime']=ereg_replace("([0-9]{4}):([0-9]{2}):([0-9]{2})()","\\1-\\2-\\3\\4",$exif_header['DateTimeOriginal']);
+   } else if($exif_header['DateTime'])  {
+   // Fall back to modify time
+   $result['Exif.DateTime']=ereg_replace("([0-9]{4}):([0-9]{2}):([0-9]{2})()","\\1-\\2-\\3\\4",$exif_header['DateTime']);
+   } else if ($exif_header['FileDateTime'])  {
+   // Fall back to file modify time
+   $result['Exif.DateTime']=ereg_replace("([0-9]{4}):([0-9]{2}):([0-9]{2})()","\\1-\\2-\\3\\4",date("Y-m-d H:i:s",$exif_header['FileDateTime']));
    }
 
+// Getting GPS data
+if ($exif_header["GPSLatitude"]){
+#echo "<pre>
+#Lat:";
+#print_r($exif_header["GPSLatitude"]);
+#echo "</pre>";
+      $Latitude=$exif_header["GPSLatitude"];
+  if ($Latitude) {
+    eval("\$latdeg=".$Latitude[0].";");
+    eval("\$latmin=".$Latitude[1].";");
+    eval("\$latsec=".$Latitude[2].";");
+    $result['Exif.Latitude']=$latdeg."  ".$latmin."' ".$latsec."''";
+  }
+} else $result['Exif.Latitude']='unknown';
+if ($exif_header["GPSLongitude"]){
+#echo "<pre>
+#Lon:";
+#print_r($exif_header["GPSLongitude"]);
+#echo "</pre>";
+      $Longitude=$exif_header["GPSLongitude"];
+  if ($Longitude) {
+    eval("\$londeg=".$Longitude[0].";");
+    eval("\$lonmin=".$Longitude[1].";");
+    eval("\$lonsec=".$Longitude[2].";");
+    $result['Exif.Longitude']=$londeg."  ".$lonmin."' ".$lonsec."''";
+  }
+} else $result['Exif.Longitude']='unknown';
+
 // Getting Exif.JpegComment
 if ($exif_header['COMMENT']) {
     // So far, I've only seen row in the COMMENT array such, no loop has been implemented
@@ -345,6 +416,22 @@
   return $iptc_ref;
 }
 
+# http://www.php.net/manual/en/function.iptcparse.php#39650
+function output_iptc_data( $image_path ) {   
+    $size = getimagesize ( $image_path, $info);       
+     if(is_array($info)) {   
+        $iptc = iptcparse($info["APP13"]);
+        foreach (array_keys($iptc) as $s) {             
+            $c = count ($iptc[$s]);
+            for ($i=0; $i <$c; $i++)
+            {
+                echo $s.' = '.$iptc[$s][$i].'<br>';
+            }
+        }                 
+    }            
+}
+
+
 function get_iptc_data($image_path) {
 
 // Read the IPTC header of a picture and return an array formatted from
@@ -358,11 +445,30 @@
 
 // Extracting IPTC header and put it in the formatted array
 
+# debug:
+##  output_iptc_data($image_path);
+
+  
   getimagesize($image_path, $imageinfo);
 
   if (is_array($imageinfo)) {
 
+#print("<pre>
+#");
+#print_r($imageinfo);
+#print("
+#</pre>");
+
       $iptc_header=iptc_parse($imageinfo["APP13"]);
+
+#$keywordcount = count($imageinfo["2#025"]);
+#for ($i=0; $i<$keywordcount; $i++) $keywords .= $imageinfo["2#025"][$i] . " ";
+#print("<pre>
+#");
+#print_r($keywords);
+#print("
+#</pre>");
+
      }
 
   if (is_array($iptc_header)) {
@@ -406,6 +512,12 @@
   $special_char="%";
   $temp_array=explode($special_char, $text);
 
+#print("<pre>
+#");
+##print_r($text);
+#print("
+#</pre>");
+
   for ($i=0;$i<sizeof($temp_array);$i++) {
 
     // Skip blank entries
@@ -445,6 +557,7 @@
     $size = getimagesize ( $image_path, $info);
 
     if(is_array($info)) {
+
         $iptc = iptcparse($info["APP13"]);
     }
 
@@ -591,4 +704,62 @@
 
 }
 
+function geotag_image($image_path, $lat, $lon, $address_txt){
+# needs PEL
+#echo "<pre>";
+      $jpeg = new PelJpeg($image_path);
+      $exif = $jpeg->getExif();
+      $tiff = $exif->getTiff();
+      $ifd0 = $tiff->getIfd();
+      $gps = $ifd0->getSubIfd(PelIfd::GPS);
+if(empty($gps)){
+  $ifd0->addSubIfd(new PelIfd(PelIfd::GPS));
+  $gps = $ifd0->getSubIfd(PelIfd::GPS);
+}
+#print_r($gps);
+#print("\n");
+#print("            lon:".$lon."\n");
+#print("            lat:".$lat."\n");
+
+//print_r($gps->getValidTags());
+//foreach ( $gps->getValidTags() as $tag){
+//print(PelTag::getName(PelIfd::GPS,$tag)."\n");
+//}
+      $exif_lat = $gps->getEntry(PelTag::GPS_LATITUDE);
+      if(empty($exif_lat)){
+        $gps->addEntry(new PelEntrySRational(PelTag::GPS_LATITUDE,array(0,1),array(0,1),array(0,1)));
+        $exif_lat = $gps->getEntry(PelTag::GPS_LATITUDE);
+      }
+#print_r($exif_lat);
+#print($exif_lat->getText()."\n");
+      //$exif_lat->setValue($lat);
+
+      $deg=floor($lat);
+      $rem=abs($deg-$lat);
+      $min=floor($rem*60);
+      $rem2=abs($min-$rem*60);
+      $sec=floor($rem2*60*1000)/1000;
+#print($deg." ".$min." ".$sec."\n");
+      $exif_lat->setValue(array($deg,1), array($min,1),array($sec*1000,1000));
+#print($exif_lat->getText()."\n");
+      $exif_lon = $gps->getEntry(PelTag::GPS_LONGITUDE);
+      if(empty($exif_lon)){
+        $gps->addEntry(new PelEntrySRational(PelTag::GPS_LONGITUDE,array(0,1),array(0,1),array(0,1)));
+        $exif_lon = $gps->getEntry(PelTag::GPS_LONGITUDE);
+      }
+#print($exif_lon->getText()."\n");
+      //$exif_lon->setValue($lon);
+      $deg=floor($lon);
+      $rem=abs($deg-$lon);
+      $min=floor($rem*60);
+      $rem2=abs($min-$rem*60);
+      $sec=floor($rem2*60*1000)/1000;
+#print($deg." ".$min." ".$sec."\n");
+      $exif_lon->setValue(array($deg,1), array($min,1),array($sec*1000,1000));
+#print($exif_lon->getText()."\n");
+#echo "</pre>";
+      unlink($image_path);
+      file_put_contents($image_path, $jpeg->getBytes());
+}
+
 ?>
===================================================================
RCS file: ./base/include/RCS/yorsh-varval-config_data.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/include/yorsh-varval-config_data.inc.php
--- ./base/include/yorsh-varval-config_data.inc.php	2010/09/24 19:59:02	1.1
+++ ./base/include/yorsh-varval-config_data.inc.php	2011/09/04 21:16:41
@@ -16,7 +16,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: yorsh-varval-config_data.inc.php,v 1.1 2010/09/24 19:59:02 root Exp $
+*  $Id: yorsh-varval-config_data.inc.php,v 1.2 2010/09/25 22:32:44 root Exp $
 *
 */
 
@@ -254,6 +254,9 @@
         'use_comments' => array(
             'constraint' => 'bool'
             ),
+        'use_gps' => array(
+            'constraint' => 'bool'
+            ),
         'use_exif' => array(
             'constraint' => 'bool'
             ),
===================================================================
RCS file: ./base/include/RCS/yorsh-varval-request_data.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/include/yorsh-varval-request_data.inc.php
--- ./base/include/yorsh-varval-request_data.inc.php	2010/09/27 22:06:37	1.1
+++ ./base/include/yorsh-varval-request_data.inc.php	2011/09/04 21:16:41
@@ -16,7 +16,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: yorsh-varval-request_data.inc.php,v 1.1 2010/09/27 22:06:37 root Exp $
+*  $Id: yorsh-varval-request_data.inc.php,v 1.2 2010/09/28 16:28:35 root Exp $
 *
 */
 
@@ -286,6 +286,17 @@
         'uid' => array(
             'constraint' => 'int',
             'empty' => 0
+            ),
+        'lat' => array(
+            'constraint' => 'string',
+            ),
+        'lon' => array(
+            'constraint' => 'string',
+            ),
+        'address_txt' => array(
+            'constraint' => 'string',
+            'empty' => 1,
+            'maxlength' => '128'
             )
         );
 
@@ -310,6 +321,9 @@
         'int' => array(
             'function' => 'is_numeric'
             ),
+        'float' => array(
+            'function' => 'is_float'
+            ),
         'string' => array(
             'pregex' => '/^.+$/' // Match everything BUT doesn't accept CR/LF and such must be used with caution
             ),
===================================================================
RCS file: ./base/lang/RCS/lang_en.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/lang/lang_en.inc.php
--- ./base/lang/lang_en.inc.php	2010/09/28 19:07:43	1.1
+++ ./base/lang/lang_en.inc.php	2011/09/04 21:16:41
@@ -18,7 +18,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: lang_en.inc.php,v 1.1 2010/09/28 19:07:43 root Exp $
+*  $Id: lang_en.inc.php,v 1.2 2010/09/29 09:31:17 root Exp $
 *
 */
 
@@ -150,6 +150,7 @@
 $txt['No_IPTC_metadata_found'] = 'No IPTC metadata found';
 
 $txt_show_me_more="Show me more";
+$txt_toggle_map="Toggle map display";
 
 // SLIDESHOW
 $txt['slideshow'] = 'slideshow';
===================================================================
RCS file: ./base/lang/RCS/lang_de.inc.php,v
retrieving revision 1.1
diff -u -r1.1 ./base/lang/lang_de.inc.php
--- ./base/lang/lang_de.inc.php	2010/09/28 19:07:32	1.1
+++ ./base/lang/lang_de.inc.php	2011/09/04 21:16:41
@@ -18,7 +18,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: lang_de.inc.php,v 1.1 2010/09/28 19:07:32 root Exp $
+*  $Id: lang_de.inc.php,v 1.3 2011/08/17 20:17:26 root Exp $
 *
 */
 
@@ -138,10 +138,11 @@
 $txt_exif_missing_value="??";	// If EXIF requested field is not found, display this instead
 $txt_exif_flash="Mit Blitz"; // Test to display if flash was fired
 
-$txt_iptc_custom="%Iptc.City% by %Iptc.ByLine%";
+$txt_iptc_custom="%Iptc.City% (%Iptc.CountryName%) %Iptc.Sublocation% by %Iptc.ByLine%";
 $txt_iptc_missing_value="";	// If IPTC requested field not found, display that instead
 
-$txt_show_me_more="Show me more";
+$txt_show_me_more="Mehr ...";
+$txt_toggle_map="Karte ein/aus";
 
 // SLIDESHOW
 $txt['Slideshow launch'] = 'Diashow starten...';
===================================================================
RCS file: ./base/js/RCS/OpenLayers.js,v
rcsdiff: ./base/js/OpenLayers.js: No such file or directory
===================================================================
RCS file: ./base/js/RCS/phpgraphy.js,v
retrieving revision 1.1
diff -u -r1.1 ./base/js/phpgraphy.js
--- ./base/js/phpgraphy.js	2011/06/19 16:47:21	1.1
+++ ./base/js/phpgraphy.js	2011/07/03 12:56:09
@@ -14,6 +14,36 @@
         document.getElementById(id).style.display = 'none';
 }
 
+function upsize_display(id)
+{
+        e = document.getElementById(id);
+        h = e.offsetHeight;
+        if(h<900)
+          h=h*2;
+        e.style.height = h + 'px';
+       // t=setTimeout("changewidth();",0);
+        google.maps.event.trigger(map,'resize');
+        if(marker)
+        map.setCenter(new google.maps.LatLng(parseFloat(marker.getPosition().lat()),
+                                             parseFloat(marker.getPosition().lng())));
+        if(latlngbounds)
+        map.fitBounds(latlngbounds);
+}
+
+function downsize_display(id)
+{
+        e = document.getElementById(id);
+        h = e.offsetHeight;
+        if(h>240)
+          h=h/2;
+        e.style.height = h + 'px';
+        google.maps.event.trigger(map,'resize');
+        if(marker)
+        map.setCenter(new google.maps.LatLng(parseFloat(marker.getPosition().lat()),
+                                             parseFloat(marker.getPosition().lng())));
+        if(latlngbounds)
+        map.fitBounds(latlngbounds);
+}
 
 function isEmpty(id)
 {
===================================================================
RCS file: ./base/js/RCS/google-maps-api-3.js,v
retrieving revision 1.1
diff -u -r1.1 ./base/js/google-maps-api-3.js
--- ./base/js/google-maps-api-3.js	2011/08/27 07:55:22	1.1
+++ ./base/js/google-maps-api-3.js	2011/08/27 07:55:44
@@ -0,0 +1,23 @@
+
+
+window.google = window.google || {};
+google.maps = google.maps || {};
+(function() {
+  
+  function getScript(src) {
+    document.write('<' + 'script src="' + src + '"' +
+                   ' type="text/javascript"><' + '/script>');
+  }
+  
+  var modules = google.maps.modules = {};
+  google.maps.__gjsload__ = function(name, text) {
+    modules[name] = text;
+  };
+  
+  google.maps.Load = function(apiLoad) {
+    delete google.maps.Load;
+    apiLoad([null,[[["http://mt0.googleapis.com/vt?lyrs=m@159000000\u0026src=api\u0026hl=en-US\u0026","http://mt1.googleapis.com/vt?lyrs=m@159000000\u0026src=api\u0026hl=en-US\u0026"],null,null,null,null,"m@159000000"],[["http://khm0.googleapis.com/kh?v=90\u0026hl=en-US\u0026","http://khm1.googleapis.com/kh?v=90\u0026hl=en-US\u0026"],null,null,null,1,"90"],[["http://mt0.googleapis.com/vt?lyrs=h@159000000\u0026src=api\u0026hl=en-US\u0026","http://mt1.googleapis.com/vt?lyrs=h@159000000\u0026src=api\u0026hl=en-US\u0026"],null,null,"imgtp=png32\u0026",null,"h@159000000"],[["http://mt0.googleapis.com/vt?lyrs=t@127,r@159000000\u0026src=api\u0026hl=en-US\u0026","http://mt1.googleapis.com/vt?lyrs=t@127,r@159000000\u0026src=api\u0026hl=en-US\u0026"],null,null,null,null,"t@127,r@159000000"],null,[[null,0,7,7,[[[330000000,1246050000],[386200000,1293600000]],[[366500000,1297000000],[386200000,1320034790]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026"]],[null,0,8,8,[[[330000000,1246050000],[386200000,1279600000]],[[345000000,1279600000],[386200000,1286700000]],[[354690000,1286700000],[386200000,1320035000]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026"]],[null,0,9,9,[[[330000000,1246050000],[386200000,1279600000]],[[340000000,1279600000],[386200000,1286700000]],[[348900000,1286700000],[386200000,1302000000]],[[368300000,1302000000],[386200000,1320035000]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026"]],[null,0,10,19,[[[329890840,1246055600],[386930130,1284960940]],[[344646740,1284960940],[386930130,1288476560]],[[350277470,1288476560],[386930130,1310531620]],[[370277730,1310531620],[386930130,1320034790]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1.15\u0026hl=en-US\u0026"]],[null,3,7,7,[[[330000000,1246050000],[386200000,1293600000]],[[366500000,1297000000],[386200000,1320034790]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026"]],[null,3,8,8,[[[330000000,1246050000],[386200000,1279600000]],[[345000000,1279600000],[386200000,1286700000]],[[354690000,1286700000],[386200000,1320035000]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026"]],[null,3,9,9,[[[330000000,1246050000],[386200000,1279600000]],[[340000000,1279600000],[386200000,1286700000]],[[348900000,1286700000],[386200000,1302000000]],[[368300000,1302000000],[386200000,1320035000]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026"]],[null,3,10,null,[[[329890840,1246055600],[386930130,1284960940]],[[344646740,1284960940],[386930130,1288476560]],[[350277470,1288476560],[386930130,1310531620]],[[370277730,1310531620],[386930130,1320034790]]],["http://mt0.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026","http://mt1.gmaptiles.co.kr/mt?v=kr1p.12\u0026hl=en-US\u0026"]]],[["http://cbk0.googleapis.com/cbk?","http://cbk1.googleapis.com/cbk?"]],[["http://khmdb0.googleapis.com/kh?v=41\u0026hl=en-US\u0026","http://khmdb1.googleapis.com/kh?v=41\u0026hl=en-US\u0026"],null,null,null,null,"41"],[["http://mt0.googleapis.com/mapslt?hl=en-US\u0026","http://mt1.googleapis.com/mapslt?hl=en-US\u0026"]],[["http://mt0.googleapis.com/mapslt/ft?hl=en-US\u0026","http://mt1.googleapis.com/mapslt/ft?hl=en-US\u0026"]],[["http://mt0.googleapis.com/vt?hl=en-US\u0026","http://mt1.googleapis.com/vt?hl=en-US\u0026"]]],["en-US","US",null,0,null,"http://maps.google.com","http://maps.gstatic.com/intl/en_us/mapfiles/","http://csi.gstatic.com","https://maps.googleapis.com","http://maps.googleapis.com"],["http://maps.gstatic.com/intl/en_us/mapfiles/api-3/6/1","3.6.1"],[1934175882],1.0,null,null,null,null,0,"",null,null,0,"http://khm.googleapis.com/mz?v=90\u0026"], loadScriptTime);
+  };
+  var loadScriptTime = (new Date).getTime();
+  getScript("http://maps.gstatic.com/intl/en_us/mapfiles/api-3/6/1/main.js");
+})();
===================================================================
RCS file: ./base/js/RCS/draggable-markers.js,v
retrieving revision 1.1
diff -u -r1.1 ./base/js/draggable-markers.js
--- ./base/js/draggable-markers.js	2011/06/23 17:52:54	1.1
+++ ./base/js/draggable-markers.js	2011/06/23 17:55:49
@@ -0,0 +1,119 @@
+//<html>
+//<head>
+//<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
+//<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
+//<script type="text/javascript">
+// http://gmaps-samples-v3.googlecode.com/svn/trunk/draggable-markers/draggable-markers.html
+var geocoder = new google.maps.Geocoder();
+var admin = false;
+var map;
+var marker;
+
+function geocodePosition(pos) {
+  geocoder.geocode({
+    latLng: pos
+  }, function(responses) {
+    if (responses && responses.length > 0) {
+      updateMarkerAddress(responses[0].formatted_address);
+    } else {
+      updateMarkerAddress('Cannot determine address at this location.');
+    }
+  });
+}
+
+function updateMarkerStatus(str) {
+    document.getElementById('markerStatus').innerHTML = str;
+}
+
+function updateMarkerPosition(latLng) {
+if (document.getElementById('lat')){
+document.getElementById('lat').value = latLng.lat();
+document.getElementById('lon').value = latLng.lng();
+} else {
+  document.getElementById('position_info').innerHTML = [
+    latLng.lat(),
+    latLng.lng()
+  ].join(', ');
+}
+}
+
+function updateMarkerAddress(str) {
+ if (document.getElementById('address_txt'))
+   document.getElementById('address_txt').value = str;
+ else
+   document.getElementById('address').innerHTML = str;
+//  if(!admin)
+//  else{
+//    document.getElementById('lat').value = latLng.lat();
+//    document.getElementById('lon').value = latLng.lng();
+//  }
+}
+
+
+    // addAddressToMap() is called when the geocoder returns an
+    // answer.  It adds a marker to the map with an open info window
+    // showing the nicely formatted version of the address and the country code.
+function addAddressToMap(responses) {
+    if (responses && responses.length > 0) {
+//alert("responses:"+responses[0].geometry.toSource());
+      if(responses[0].geometry){
+      marker.setPosition(responses[0].geometry.location);
+      map.setCenter(responses[0].geometry.location);
+      updateMarkerPosition(responses[0].geometry.location);
+      } else {updateMarkerAddress('Cannot determine this location.');}
+//        marker.openInfoWindowHtml(place.address + '<br>' +
+//          '<b>Country code:</b> ' + place.AddressDetails.Country.CountryNameCode);
+//      }
+    } else {
+      updateMarkerAddress('Cannot determine this location.');
+    }
+}
+
+
+
+    // showLocation() is called when you click on the Search button
+    // in the form.  It geocodes the address entered into the form
+    // and adds a marker to the map at that location.
+    function showLocation() {
+      var address = document.getElementById('address_txt').value;
+      geocoder.geocode({address: address}, addAddressToMap);
+    }
+ 
+  
+
+  function map_initialize(latitude_dec,longitude_dec,admin,initialzoom,initial) {
+    var latlng = new google.maps.LatLng(latitude_dec,longitude_dec);
+
+
+    var myOptions = {
+      zoom: initialzoom,
+      center: latlng,
+      mapTypeId: google.maps.MapTypeId.HYBRID
+    };
+
+    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
+    marker = new google.maps.Marker({
+        position: latlng, 
+        map: map, 
+        title:"Picture Location",
+        draggable: admin,
+    }); 
+    // Update current position info.
+    updateMarkerPosition(latlng);
+    geocodePosition(latlng);
+    
+    // Add dragging event listeners.
+    google.maps.event.addListener(marker, 'dragstart', function() {
+      updateMarkerAddress('Dragging...');
+    });
+    
+    google.maps.event.addListener(marker, 'drag', function() {
+      updateMarkerStatus('Dragging...');
+      updateMarkerPosition(marker.getPosition());
+    });
+    
+    google.maps.event.addListener(marker, 'dragend', function() {
+      updateMarkerStatus('Drag ended');
+      geocodePosition(marker.getPosition());
+    });
+  }
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/agfa.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/agfa.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/agfa.php	2011/08/17 20:11:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/agfa.php	2004/08/01 16:20:14
@@ -0,0 +1,117 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     agfa.php
+*
+* Description:  Agfa Makernote Parser
+*               Provides functions to decode an Agfa EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Agfa Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          8 Bytes         "AGFA \x00\x01"
+*               IFD Data        Variable        Standard IFD Data using Olympus Tags
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Agfa makernote uses Olympus tags - ensure they are included
+
+include_once 'olympus.php';
+
+
+
+// Add the Parser function to the list of Makernote Parsers. (Interpreter Functions are supplied by the Olympus script)
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Agfa_Makernote";
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Agfa_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Agfa_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Agfa
+        if ( stristr( $Make_Field, "Agfa" ) === FALSE )
+        {
+                // The Make Field doesnt contain the word Agfa
+                return FALSE;
+        }
+
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 7 ) != "AGFA \x00\x01" )
+        {
+                // This isn't a Agfa Makernote, abort
+                return FALSE ;
+        }
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 );
+
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Agfa";
+        $Makernote_Tag['Makernote Tags'] = "Olympus";
+
+        // Return the new tag
+        return $Makernote_Tag;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Agfa_Makernote
+******************************************************************************/
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/casio.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/casio.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/casio.php	2011/08/17 20:12:03	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/casio.php	2005/01/20 22:47:46
@@ -0,0 +1,575 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     casio.php
+*
+* Description:  Casio Makernote Parser
+*               Provides functions to decode an Casio EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Casio Makernote Format:
+*
+*               Type 1:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               IFD Data        Variable        Standard IFD Data using Casio Type 1 Tags and Motorola Byte Alignment
+*               ----------------------------------------------------------------
+*
+*               Type 2:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          6 Bytes         "QVC\x00\x00\x00"
+*               IFD Data        Variable        Standard IFD Data using Casio Type 2 Tags and Motorola Byte Alignment
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.11 : changed get_Casio_Makernote_Html to allow thumbnail links to work when
+*                              toolkit is portable across directories
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Casio_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Casio_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Casio_Makernote_Html";
+
+include_once dirname(__FILE__) .'/../pjmt_utils.php';          // Change: as of version 1.11 - added to allow directory portability
+
+
+/******************************************************************************
+*
+* Function:     get_Casio_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Casio_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Casio
+        if ( stristr( $Make_Field, "Casio" ) === FALSE )
+        {
+                return FALSE;
+        }
+
+
+        if ( substr( $Makernote_Tag['Data'],0 , 6 ) == "QVC\x00\x00\x00" )
+        {
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 );
+
+                $Makernote_Tag['ByteAlign'] = "MM";
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Casio Type 2";
+                $Makernote_Tag['Makernote Tags'] = "Casio Type 2";
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+        else
+        {
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 );
+
+                $Makernote_Tag['ByteAlign'] = "MM";
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 1" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Casio Type 1";
+                $Makernote_Tag['Makernote Tags'] = "Casio Type 1";
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+}
+
+/******************************************************************************
+* End of Function:     get_Casio_Makernote
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Casio_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Casio_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Casio_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+
+        // Check that this tag uses the Casio tag definitions, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name == "Casio Type 2" )
+        {
+                // Tag Uses Casio Type 2 Tag definitions
+                // Process the tag according to it's tag number
+                if ( $Exif_Tag['Tag Number'] == 0x001D )
+                {
+                        return  $Exif_Tag['Data'][0]/10 . $Exif_Tag['Units'];
+                }
+                else
+                {
+                        return FALSE;
+                }
+        }
+        else if ( $Tag_Definitions_Name == "Casio Type 1" )
+        {
+                // Tag Uses Casio Type 1 Tags
+                return FALSE;
+        }
+        else
+        {
+                // Tag does NOT use Casio Tag definitions
+                return FALSE;
+        }
+
+        // Shouldn't get here
+        return FALSE;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Casio_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Casio_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Casio_Makernote_Html( $Makernote_tag, $filename )
+{
+        // Check that this tag uses the Casio tags, otherwise it can't be interpreted here
+        if ( ( $Makernote_tag['Makernote Type'] != "Casio Type 1" ) &&
+             ( $Makernote_tag['Makernote Type'] != "Casio Type 2" ) )
+        {
+                // Not Casio tags - can't interpret with this function
+                return FALSE;
+        }
+
+        // Casio Thumbnail (Tag 4)
+        if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) &&
+             ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) )
+        {
+                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                // Build the path of the thumbnail script and its filename parameter to put in a url
+                $link_str = get_relative_path( dirname(__FILE__) . "/get_casio_thumb.php" , getcwd ( ) );
+                $link_str .= "?filename=";
+                $link_str .= get_relative_path( $filename, dirname(__FILE__) );
+
+                // Add thumbnail link to html
+                $Makernote_tag['Decoded Data'][0][4]['Text Value'] = "<a class=\"EXIF_Casio_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_Casio_Thumb\" src=\"$link_str\"></a></td></tr>\n";
+                $Makernote_tag['Decoded Data'][0][4]['Type'] = "String";
+        }
+
+
+        // Casio Thumbnail (Tag 8192)
+        if ( ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) &&
+             ( $Makernote_tag['Makernote Tags'] == "Casio Type 2" ) )
+        {
+                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                // Build the path of the thumbnail script and its filename parameter to put in a url
+                $link_str = get_relative_path( dirname(__FILE__) . "/.." . "/get_casio_thumb.php" , getcwd ( ) );
+                $link_str .= "?filename=";
+                $link_str .= get_relative_path( $filename, dirname(__FILE__) . "/.." );
+
+                // Add thumbnail link to html
+                $Makernote_tag['Decoded Data'][0][8192]['Text Value'] = "<a class=\"EXIF_Casio_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_Casio_Thumb\" src=\"$link_str\"></a></td></tr>\n";
+                $Makernote_tag['Decoded Data'][0][8192]['Type'] = "String";
+        }
+
+
+        // Check if there are two thumbnail offset tags
+        if ( ( array_key_exists( 4, $Makernote_tag['Decoded Data'][0] ) ) &&
+             ( array_key_exists( 8192, $Makernote_tag['Decoded Data'][0] ) ) )
+        {
+                // There are two copies of the thumbnail offset - Remove one
+                array_splice( $Makernote_tag['Decoded Data'][0], 4, 1);
+        }
+
+
+        // Interpret the IFD and return the html
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Casio_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Casio Type 1
+*
+* Contents:     This global variable provides definitions of the known Casio Type 1
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 1"] = array(
+
+1 => array(     'Name' => "Recording Mode",
+                'Type' => "Lookup",
+                1 => "Single Shutter",
+                2 => "Panorama",
+                3 => "Night Scene",
+                4 => "Portrait",
+                5 => "Landscape" ),
+
+2 => array(     'Name' => "Quality",
+                'Type' => "Lookup",
+                1 => "Economy",
+                2 => "Normal",
+                3 => "Fine" ),
+
+3 => array(     'Name' => "Focusing Mode",
+                'Type' => "Lookup",
+                2 => "Macro",
+                3 => "Auto Focus",
+                4 => "Manual Focus",
+                5 => "Infinity" ),
+
+4 => array(     'Name' => "Flash Mode",
+                'Type' => "Lookup",
+                1 => "Auto",
+                2 => "On",
+                3 => "Off",
+                4 => "Off" ),
+
+5 => array(     'Name' => "Flash Intensity",
+                'Type' => "Lookup",
+                11 => "Weak",
+                13 => "Normal",
+                15 => "Strong" ),
+
+6 => array(     'Name' => "Object Distance",
+                'Type' => "Numeric",
+                'Units' => "mm" ),
+
+7 => array(     'Name' => "White Balance",
+                'Type' => "Lookup",
+                1 => "Auto",
+                2 => "Tungsten",
+                3 => "Daylight",
+                4 => "Flourescent",
+                5 => "Shade",
+                129 => "Manual" ),
+
+10 => array(    'Name' => "Digital Zoom",
+                'Type' => "Lookup",
+                0x10000 => "Off",
+                0x10001 => "2x Digital Zoom",
+                0x20000 => "2x Digital Zoom",
+                0x40000 => "4x Digital Zoom" ),
+
+11 => array(    'Name' => "Sharpness",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Soft",
+                2 => "Hard" ),
+
+12 => array(    'Name' => "Contrast",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Low",
+                2 => "High" ),
+
+13 => array(    'Name' => "Saturation",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Low",
+                2 => "High" ),
+
+20 => array(    'Name' => "CCD Sensitivity",
+                'Type' => "Lookup",
+                64 => "Normal",
+                125 => "+1.0",
+                250 => "+2.0",
+                244 => "+3.0",
+                80 => "Normal (ISO 80 equivalent)",
+                100 => "High" ),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Casio Type 1
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Casio Type 2
+*
+* Contents:     This global variable provides definitions of the known Casio Type 2
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Casio Type 2"] = array(
+
+0x0002 => array(        'Name' => "Preview Thumbnail Dimensions",
+                        'Type' => "Numeric",
+                        'Units' => "(x,y pixels)" ),
+
+0x0003 => array(        'Name' => "Preview Thumbnail Size",
+                        'Type' => "Numeric",
+                        'Units' => "bytes" ),
+
+0x0004 => array(        'Name' => "Preview Thumbnail",    // thumbnail offset
+                        'Type' => "Numeric" ),
+
+
+0x0008 => array(        'Name' => "Quality Mode",
+                        'Type' => "Lookup",
+                        1 => "Fine",
+                        2 => "Super Fine" ),
+
+0x0009 => array(        'Name' => "Image Size",
+                        'Type' => "Lookup",
+                        20 => "2288 x 1712 pixels",
+                        36 => "3008 x 2008 pixels",
+                        5 => "2048 x 1536 pixels",
+                        4 => "1600 x 1200 pixels",
+                        21 => "2592 x 1944 pixels",
+                        0 => "640 x 480 pixels",
+                        22 => "2304 x 1728 pixels" ),
+
+0x000D => array(        'Name' => "Focus Mode",
+                        'Type' => "Lookup",
+                        0 => "Normal",
+                        1 => "Macro" ),
+
+
+0x0014 => array(        'Name' => "Iso Sensitivity",
+                        'Type' => "Lookup",
+                        3 => "50",
+                        4 => "64",
+                        6 => "100",
+                        9 => "200" ),
+
+
+0x0019 => array(        'Name' => "White Balance",
+                        'Type' => "Lookup",
+                        0 => "Auto",
+                        1 => "Daylight",
+                        2 => "Shade",
+                        3 => "Tungsten",
+                        4 => "Fluorescent",
+                        5 => "Manual" ),
+
+0x001D => array(        'Name' => "Focal Length",
+                        'Type' => "Special",
+                        'Units' => "mm" ),
+
+0x001F => array(        'Name' => "Saturation",
+                        'Type' => "Lookup",
+                        0 => "-1",
+                        1 => "Normal",
+                        2 => "+1", ),
+
+0x0020 => array(        'Name' => "Contrast",
+                        'Type' => "Lookup",
+                        0 => "-1",
+                        1 => "Normal",
+                        2 => "+1", ),
+
+0x0021 => array(        'Name' => "Sharpness",
+                        'Type' => "Lookup",
+                        0 => "-1",
+                        1 => "Normal",
+                        2 => "+1", ),
+
+
+0x0e00 => array(        'Name' => "Print Image Matching Info",
+                        'Type' => "PIM" ),
+
+
+0x2000 => array(        'Name' => "Casio Preview Thumbnail",    // thumbnail offset
+                        'Type' => "String" ),
+
+
+
+0x2011 => array(        'Name' => "White Balance Bias",
+                        'Type' => "Numeric" ),
+
+
+0x2012 => array(        'Name' => "White Balance",
+                        'Type' => "Lookup",
+                        12 => "Flash",
+                        0 =>  "Manual",
+                        1 => "Auto?",
+                        4 => "Flash?", ),
+
+0x2022 => array(        'Name' => "Object Distance",
+                        'Type' => "Numeric",
+                        'Units' => "mm" ),
+
+
+0x2034 => array(        'Name' => "Flash Distance",
+                        'Type' => "Numeric",
+                        'Units' => "   (0=Off)" ),
+
+0x3000 => array(        'Name' => "Record Mode",
+                        'Type' => "Lookup",
+                        2 => "Normal Mode" ),
+
+0x3001 => array(        'Name' => "Self Timer?",
+                        'Type' => "Lookup",
+                        1 => "Off?" ),
+
+
+0x3002 => array(        'Name' => "Quality",
+                        'Type' => "Lookup",
+                        3 => "Fine" ),
+
+0x3003 => array(        'Name' => "Focus Mode",
+                        'Type' => "Lookup",
+                        6 => "Multi-Area Auto Focus",
+                        1 => "Fixation" ),
+
+
+0x3006 => array(        'Name' => "Time Zone",
+                        'Type' => "String" ),
+
+
+0x3007 => array(        'Name' => "Bestshot Mode",
+                        'Type' => "Lookup",
+                        0 => "Off",
+                        1 => "On?" ),
+
+
+0x3014 => array(        'Name' => "CCD ISO Sensitivity",
+                        'Type' => "Numeric" ),
+
+
+
+0x3015 => array(        'Name' => "Colour Mode",
+                        'Type' => "Lookup",
+                        0 => "Off" ),
+
+
+0x3016 => array(        'Name' => "Enhancement",
+                        'Type' => "Lookup",
+                        0 => "Off" ),
+
+0x3017 => array(        'Name' => "Filter",
+                        'Type' => "Lookup",
+                        0 => "Off" ),
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Casio Type 2
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/ricoh.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/ricoh.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/ricoh.php	2011/08/17 20:12:05	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/ricoh.php	2004/11/22 21:26:56
@@ -0,0 +1,415 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     ricoh.php
+*
+* Description:  Ricoh Makernote Parser
+*               Provides functions to decode an Ricoh EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Ricoh Makernote Format:
+*
+*               Type 1 - Text Makernote
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Text            Variable        Text, beginning with "Rv" or "Rev"
+*               ---------------------------------------------------------------
+*
+*
+*               Type 2 - Empty Makernote
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Blank           Variable        Blank field filled with 0x00 characters
+*               ---------------------------------------------------------------*
+*
+*
+*               Type 3 - IFD Makernote
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          5 Bytes        "Ricoh" or "RICOH"
+*               Unknown         1 Byte          Unknown field
+*               Zeros           2 Bytes         Two 0x00 characters
+*               IFD Data        Variable        Standard IFD Data using Ricoh Tags
+*                                               with Motorola byte alignment
+*               ---------------------------------------------------------------
+*
+*               Within Makernote Type 3, Tag 0x2001 is the Ricoh Camera Info Sub-IFD
+*               It has the following format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          19 Bytes        "[Ricoh Camera Info]"
+*               Unknown         1 Byte          Unknown field
+*               IFD Data        Variable        NON-Standard IFD Data using Ricoh
+*                                               Sub-IFD Tags with Motorola byte alignment
+*                                               and has No final Next-IFD pointer
+*               ---------------------------------------------------------------
+*
+*
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Ricoh_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Ricoh_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Ricoh_Makernote_Html";
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Ricoh_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Ricoh_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Ricoh
+        if ( stristr( $Make_Field, "Ricoh" ) === FALSE )
+        {
+                // Ricoh not in the Maker field - abort
+                return FALSE;
+        }
+
+
+        // Check if the Text Makernote header exists at the start of the Makernote
+        if ( ( substr( $Makernote_Tag['Data'], 0, 2 ) === "Rv" ) ||
+             ( substr( $Makernote_Tag['Data'], 0, 3 ) === "Rev" ) )
+        {
+                // This is a text makernote - Label it as such
+                $Makernote_Tag['Makernote Type'] = "Ricoh Text";
+                $Makernote_Tag['Makernote Tags'] = "None";
+                $Makernote_Tag['Decoded'] = TRUE;
+
+                // Return the new Makernote tag
+                return $Makernote_Tag;
+
+        }
+        // Check if the Empty Makernote header exists at the start of the Makernote
+        else if ( $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) )
+        {
+                // This is an Empty Makernote - Label it as such
+                $Makernote_Tag['Makernote Type'] = "Ricoh Empty Makernote";
+                $Makernote_Tag['Makernote Tags'] = "None";
+                $Makernote_Tag['Decoded'] = TRUE;
+
+                // Return the new Makernote tag
+                return $Makernote_Tag;
+
+        }
+        // Check if the IFD Makernote header exists at the start of the Makernote
+        else if ( ( substr( $Makernote_Tag['Data'], 0, 5 ) === "RICOH" ) ||
+                  ( substr( $Makernote_Tag['Data'], 0, 5 ) === "Ricoh" ) )
+        {
+                //This is an IFD Makernote
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 );
+
+                // Ricoh Makernote always uses Motorola Byte Alignment
+                $Makernote_Tag['ByteAlign'] = "MM";
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Ricoh" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Ricoh";
+                $Makernote_Tag['Makernote Tags'] = "Ricoh";
+
+                // Ricoh Makernotes can have a tag 0x2001 which is a Sub-IFD
+                // Check if the tag exists
+                if  ( ( $Makernote_Tag['Decoded Data'][0] !== FALSE ) &&
+                      ( array_key_exists( 0x2001, $Makernote_Tag['Decoded Data'][0] ) ) )
+                {
+                        // Ricoh Sub-IFD tag exists - Process it
+
+                        // Grab the Sub-IFD tag for easier processing
+                        $SubIFD_Tag = &$Makernote_Tag['Decoded Data'][0][0x2001];
+
+                        // Check if the Sub-IFD starts with the correct header
+                        if ( substr( $SubIFD_Tag['Data'], 0, 19 ) === "[Ricoh Camera Info]" )
+                        {
+                                // Correct Header found
+
+                                // Seek to the start of the Sub-IFD
+                                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $SubIFD_Tag['Offset'] + 20 );
+
+                                // Ricoh Makernote Sub-IFD always uses Motorola Byte Alignment
+                                $SubIFD_Tag['ByteAlign'] = "MM";
+
+
+                                // Read the IFD(s) into an array
+                                $SubIFD_Tag['Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $SubIFD_Tag['ByteAlign'], "RicohSubIFD", False, False );
+
+                                // Save some information into the Tag element to aid interpretation
+                                $SubIFD_Tag['Decoded'] = TRUE;
+                                $SubIFD_Tag['Makernote Type'] = "Ricoh";
+                                $SubIFD_Tag['Makernote Tags'] = "RicohSubIFD";
+
+                                // Change the tag type to a Sub-IFD so it is handled automatically for interpretation
+                                $SubIFD_Tag['Type'] = "SubIFD";
+                                $SubIFD_Tag['Tags Name'] = "RicohSubIFD";
+
+                        }
+                        else
+                        {
+                                // Couldn't find header of Sub-IFD - Probably corrupt
+                                $SubIFD_Tag['Type'] = "String";
+                                $SubIFD_Tag['Text Value'] = "Corrupted Ricoh Sub IFD";
+                        }
+
+                }
+
+                // Return the new makernote tag
+                return $Makernote_Tag;
+        }
+        else
+        {
+                // Unrecognised header for makernote - abort
+                return FALSE;
+        }
+
+        // Shouldn't get here
+        return False;
+}
+
+/******************************************************************************
+* End of Function:     get_Ricoh_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Ricoh_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Ricoh_Makernote_Html( $Makernote_tag, $filename )
+{
+
+        // Check if this makernote is Ricoh IFD type
+        if ( $Makernote_tag['Makernote Type'] == "Ricoh" )
+        {
+                // This is a Ricoh IFD makernote - interpret it
+                return  interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+        }
+        // Check if this makernote is Ricoh Text type
+        else if ( $Makernote_tag['Makernote Type'] == "Ricoh Text" )
+        {
+                // This is a Ricoh text makernote
+                //  Construct the start of enclosing html for the text
+                $output_str = "<table  class=\"EXIF_Table\"border=1><tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Value_Cell\">";
+
+                // Replace the semicolon dividers with line break html tags
+                $output_str .= str_replace ( ";", "<BR>\n", $Makernote_tag['Data'] );
+
+                // Close the html
+                $output_str .= "</td></tr></table>";
+
+                // Return the html
+                return  $output_str;
+        }
+        // Check if this makernote is a Ricoh Empty makernote
+        else if ( $Makernote_tag['Makernote Type'] == "Ricoh Empty Makernote" )
+        {
+                // Do Nothing
+                return "";
+        }
+        else
+        {
+                // Don't recognise the Makernote type - not a Ricoh makernote
+                return FALSE;
+        }
+
+        // shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Ricoh_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Ricoh_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Ricoh_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Ricoh_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+
+        // Check that this tag uses the Ricoh tags, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name == "Ricoh" )
+        {
+
+                // Process the tag acording to it's tag number, to produce a text value
+                if ( $Exif_Tag['Tag Number'] == 0x0002 ) // Version tag
+                {
+                        $tmp = implode ( "\x00", $Exif_Tag['Data']);
+                        return "\"" .HTML_UTF8_Escape( $tmp ) . "\" (" . bin2hex( $tmp ) . " hex)";
+                }
+
+        }
+
+        // Unknown tag or tag definitions
+        return FALSE;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Ricoh_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Ricoh
+*
+* Contents:     This global variable provides definitions of the known Ricoh
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Ricoh"] = array(
+
+
+0x0001 => array(        'Name' => "Makernote Data Type",
+                        'Type' => "String" ),
+
+0x0002 => array(        'Name' => "Version",
+                        'Type' => "Special" ),
+
+0x0e00 => array(        'Name' => "Print Image Matching Info",
+                        'Type' => "PIM" ),
+
+0x2001 => array(        'Name' => "Ricoh Camera Info Makernote Sub-IFD",
+                        'Type' => "Special" ),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Ricoh
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, RicohSubIFD
+*
+* Contents:     This global variable provides definitions of the known Ricoh
+*               Camera Info Sub-IFD Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["RicohSubIFD"] = array(
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, RicohSubIFD
+******************************************************************************/
+
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/panasonic.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/panasonic.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/panasonic.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/panasonic.php	2004/08/04 15:59:02
@@ -0,0 +1,292 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     panasonic.php
+*
+* Description:  Panasonic Makernote Parser
+*               Provides functions to decode a Panasonic EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Panasonic Makernote Format:
+*
+*               Type 1  - IFD form
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          12 Bytes        "Panasonic\x00\x00\x00"
+*               IFD Data        Variable        NON-Standard IFD Data using Panasonic Tags
+*                                               There is no Next-IFD pointer after the IFD
+*               ----------------------------------------------------------------
+*
+*               Type 2  - Blank (Header only)
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          4 Bytes         "MKED"
+*               Junk            1 or 2 bytes    Blank or Junk data
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Panasonic_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Panasonic_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Panasonic_Makernote_Html";
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Panasonic_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Panasonic_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Panasonic
+        if ( stristr( $Make_Field, "Panasonic" ) === FALSE )
+        {
+                // No Panasonic in the maker - abort
+                return FALSE;
+        }
+        
+
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 4 ) == "MKED" )
+        {
+                // Panasonic Type 2 - Empty Makernote
+                // No Makernote Data
+                $Makernote_Tag['Makernote Type'] = "Panasonic Empty Makernote";
+                $Makernote_Tag['Makernote Tags'] = "-";
+                $Makernote_Tag['Decoded'] = TRUE;
+                
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+        else if ( substr( $Makernote_Tag['Data'], 0, 12 ) == "Panasonic\x00\x00\x00" )
+        {
+                // Panasonic Type 1 - IFD Makernote
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 12 );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Panasonic", FALSE, FALSE );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Panasonic";
+                $Makernote_Tag['Makernote Tags'] = "Panasonic";
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+        else
+        {
+                // Unknown Header
+                return FALSE;
+        }
+
+        // Shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Panasonic_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Panasonic_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Panasonic_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Panasonic_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+
+        // Check that this tag uses the Olympus tags, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name == "Panasonic" )
+        {
+                // No Special Tags yet
+                return FALSE;
+        }
+        
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Panasonic_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Panasonic_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Panasonic_Makernote_Html( $Makernote_tag, $filename )
+{
+        if ( $Makernote_tag['Makernote Type'] == "Panasonic" )
+        {
+                return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+        }
+        else if ( $Makernote_tag['Makernote Type'] == "Panasonic Empty Makernote" )
+        {
+                // Do Nothing
+                return "";
+        }
+        else
+        {
+                // Unknown Makernote Type
+                return FALSE;
+        }
+
+        // Shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Panasonic_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Panasonic
+*
+* Contents:     This global variable provides definitions of the known Panasonic
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Panasonic"] = array(
+
+0x01 => array(  'Name' => "Quality Mode",
+                'Type' => "Numeric" ),
+
+0x02 => array(  'Name' => "Version",
+                'Type' => "String" ),
+
+0x1c => array(  'Name' => "Macro Mode",
+                'Type' => "Lookup",
+                1 => "On",
+                2 => "Off" ),
+
+0x1f => array(  'Name' => "Record Mode",
+                'Type' => "Lookup",
+                1 => "Normal",
+                2 => "Portrait",
+                9 => "Macro" ),
+
+0xE00 => array( 'Name' => "Print Image Matching Info",
+                'Type' => "PIM" ),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Panasonic
+******************************************************************************/
+
+
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/fujifilm.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/fujifilm.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/fujifilm.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/fujifilm.php	2004/08/03 14:00:22
@@ -0,0 +1,344 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     fujifilm.php
+*
+* Description:  Fujifilm Makernote Parser
+*               Provides functions to decode an Fujifilm EXIF makernote and to interpret
+*               the resulting array into html.
+*               This Makernote format is also used by one Nikon Camera
+*
+*               Fujifilm Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          8 Bytes         "FUJIFILM"
+*               IFD Offset      4 Bytes         Intel Byte aligned offset to IFD from start of Makernote
+*               IFD Data        Variable        NON-Standard IFD Data using Fujifilm Tags
+*                                               Offsets are relative to start of makernote
+*                                               Byte alignment is always Intel
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Fujifilm_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Fujifilm_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Fujifilm_Makernote_Html";
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Fujifilm_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Fujifilm_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Fuji or Nikon (One Nikon camera uses this format Makernote)
+        if ( ( stristr( $Make_Field, "Fuji" ) === FALSE ) &&
+             ( stristr( $Make_Field, "Nikon" ) === FALSE ) )
+        {
+                // Couldn't find Fuji or Nikon in the maker name - abort
+                return FALSE;
+        }
+        
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 8 ) != "FUJIFILM" )
+        {
+                // This isn't a Fuji Makernote, abort
+                return FALSE;
+        }
+
+        // The 4 bytes after the header are the offset to the Fujifilm IFD
+        // Get the offset of the IFD
+        $ifd_offset = hexdec( bin2hex( strrev( substr( $Makernote_Tag['Data'], 8, 4 ) ) ) );
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + $ifd_offset );
+
+        // Fuji Makernotes are always Intel Byte Aligned
+        $Makernote_Tag['ByteAlign'] = "II";
+        
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'], $Makernote_Tag['ByteAlign'], "Fujifilm" );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Fujifilm";
+        $Makernote_Tag['Makernote Tags'] = "Fujifilm";
+
+
+        // Return the new tag
+        return $Makernote_Tag;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Fujifilm_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Fujifilm_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Fujifilm_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Fujifilm_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this tag uses the Fujifilm tag Definitions, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name == "Fujifilm" )
+        {
+                // No special Tags at this time
+                return FALSE;
+        }
+        
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Fujifilm_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Fujifilm_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Fujifilm_Makernote_Html( $Makernote_tag, $filename )
+{
+        // Check that this tag uses the Fujifilm tags, otherwise it can't be interpreted here
+        if ( $Makernote_tag['Makernote Type'] != "Fujifilm" )
+        {
+                // Not Fujifilm tags - can't interpret with this function
+                return FALSE;
+        }
+        
+        // Interpret the IFD normally
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Fujifilm_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Fujifilm
+*
+* Contents:     This global variable provides definitions of the known Fujifilm
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Fujifilm"] = array(
+
+0 => array(     'Name' => "Version",
+                'Type' => "String" ),
+
+4096 => array(  'Name' => "Quality",
+                'Type' => "String" ),
+
+4097 => array(  'Name' => "Sharpness",
+                'Type' => "Lookup",
+                1 => "Softest",
+                2 => "Soft",
+                3 => "Normal",
+                4 => "Hard",
+                5 => "Hardest" ),
+
+4098 => array(  'Name' => "White Balance",
+                'Type' => "Lookup",
+                0 => "Auto",
+                256 => "Daylight",
+                512 => "Cloudy",
+                768 => "DaylightColour-fluorescence",
+                769 => "DaywhiteColour-fluorescence",
+                770 => "White-fluorescence",
+                1024 => "Incandenscense",
+                3840 => "Custom white balance" ),
+
+4099 => array(  'Name' => "Colour Saturation",
+                'Type' => "Lookup",
+                0 => "Normal",
+                256 => "High",
+                512 => "Low" ),
+
+4100 => array(  'Name' => "Tone (Contrast)",
+                'Type' => "Lookup",
+                0 => "Normal",
+                256 => "High",
+                512 => "Low" ),
+
+4112 => array(  'Name' => "Flash Mode",
+                'Type' => "Lookup",
+                0 => "Auto",
+                1 => "On",
+                2 => "Off",
+                3 => "Red-eye Reduction" ),
+
+4113 => array(  'Name' => "Flash Strength",
+                'Type' => "Numeric",
+                'Units' => "EV" ),
+
+4128 => array(  'Name' => "Macro",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "On" ),
+
+4129 => array(  'Name' => "Focus Mode",
+                'Type' => "Lookup",
+                0 => "Auto Focus",
+                1 => "Manual Focus" ),
+
+4144 => array(  'Name' => "Slow Sync",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "On" ),
+
+4145 => array(  'Name' => "Picture Mode",
+                'Type' => "Lookup",
+                0 => "Auto",
+                1 => "Portrait Scene",
+                2 => "Landscape Scene",
+                4 => "Sports Scene",
+                5 => "Night Scene",
+                6 => "Program AE",
+                256 => "Aperture priority AE",
+                512 => "Shutter priority AE",
+                768 => "Manual Exposure" ),
+
+4352 => array(  'Name' => "Continuous taking or auto bracketing mode",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "On" ),
+
+4864 => array(  'Name' => "Blur Warning",
+                'Type' => "Lookup",
+                0 => "No Blur Warning",
+                1 => "Blur Warning" ),
+
+4865 => array(  'Name' => "Focus warning",
+                'Type' => "Lookup",
+                0 => "Auto Focus Good",
+                1 => "Out of Focus" ),
+
+4866 => array(  'Name' => "Auto Exposure Warning",
+                'Type' => "Lookup",
+                0 => "Auto Exposure Good",
+                1 => "Over exposure (>1/1000s,F11)" )
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Fujifilm
+******************************************************************************/
+
+
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/sony.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/sony.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/sony.php	2011/08/17 20:12:05	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/sony.php	2004/08/05 09:31:34
@@ -0,0 +1,244 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     sony.php
+*
+* Description:  Sony Makernote Parser
+*               Provides functions to decode an Sony EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Sony Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          12 Bytes        "SONY CAM \x00\x00\x00"
+*                                               or
+*                                               "SONY DSC \x00\x00\x00"
+*               IFD Data        Variable        NON-Standard IFD Data using Sony Tags
+*                                               IFD has no Next-IFD pointer at end of IFD,
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Sony_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Sony_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Sony_Makernote_Html";
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Sony_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Sony_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Sony
+        if ( stristr( $Make_Field, "Sony" ) === FALSE )
+        {
+                // This isn't a Sony makernote
+                return FALSE;
+        }
+                
+        // Check if the header exists at the start of the Makernote
+        if ( ( substr( $Makernote_Tag['Data'], 0, 12 ) != "SONY CAM \x00\x00\x00" ) &&
+             ( substr( $Makernote_Tag['Data'], 0, 12 ) != "SONY DSC \x00\x00\x00" ) )
+        {
+                // This isn't a Sony Makernote, abort
+                return FALSE ;
+        }
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 12 );
+
+        // Read the IFD(s) into an array
+
+
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Sony", FALSE, FALSE );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Sony";
+        $Makernote_Tag['Makernote Tags'] = "sony";
+
+
+        // Return the new tag
+        return $Makernote_Tag;
+
+
+}
+
+/******************************************************************************
+* End of Function:     get_Sony_Makernote
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Sony_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Sony_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Sony_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this tag uses the Sony tags, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name == "Sony" )
+        {
+                // No Special Tags yet
+                return FALSE;
+        }
+
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Sony_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Sony_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Sony_Makernote_Html( $Makernote_tag, $filename )
+{
+
+        // Check that this tag uses the Sony tags, otherwise it can't be interpreted here
+        if ( $Makernote_tag['Makernote Type'] != "Sony" )
+        {
+                // Not Sony tags - can't interpret with this function
+                return FALSE;
+        }
+
+        // Interpret the IFD and return the HTML
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Sony_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Sony
+*
+* Contents:     This global variable provides definitions of the known Sony
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Sony"] = array(
+
+0xE00 => array( 'Name' => "Print Image Matching Info",
+                'Type' => "PIM" ),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Sony
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/konica_minolta.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/konica_minolta.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/konica_minolta.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/konica_minolta.php	2004/08/06 22:01:14
@@ -0,0 +1,745 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     konica_minolta.php
+*
+* Description:  Konica/Minolta Makernote Parser
+*               Provides functions to decode an Konica/Minolta EXIF makernote and
+*               to interpret the resulting array into html.
+*
+*               Konica/Minolta Makernote Formats:
+*
+*               Type 1:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          3 Bytes         "MLY"
+*               Unknown Data    Variable        Unknown Data
+*               ----------------------------------------------------------------
+*
+*               Type 2:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          2 Bytes         "KC"
+*               Unknown Data    Variable        Unknown Data
+*               ----------------------------------------------------------------
+*
+*               Type 3:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          8 Bytes         "+M+M+M+M"
+*               Unknown Data    Variable        Unknown Data
+*               ----------------------------------------------------------------
+*
+*               Type 4:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          5 Bytes         "MINOL"
+*               Unknown Data    Variable        Unknown Data
+*               ----------------------------------------------------------------
+*
+*               Type 5:   NO HEADER
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               IFD Data        Variable        Standard IFD with Olympus Tags
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+// Konica/Minolta makernote uses Olympus tags - ensure they are included
+
+include_once 'olympus.php';
+
+
+// Add the parser functions to the list of Makernote parsers . (Interpreting done by Olympus script)
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Minolta_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Minolta_Text_Value";
+
+
+
+/******************************************************************************
+*
+* Function:     get_Minolta_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Minolta_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        if ( ( stristr( $Make_Field, "Konica" ) === FALSE ) &&
+             ( stristr( $Make_Field, "Minolta" ) === FALSE ) )
+        {
+                // Not a Konica/Minolta Makernote - Cannot decode it
+                return False;
+        }
+
+        // There are several different headers for a Konica/Minolta Makernote
+        // Unfortunately only one type can be decoded (the one without a header)
+        // Check which header exists (if any)
+        if ( substr( $Makernote_Tag['Data'], 0, 3 ) == "MLY" )
+        {
+                // MLY Header - Can't Decode this
+                return $Makernote_Tag;
+        }
+        else if ( substr( $Makernote_Tag['Data'], 0, 2 ) == "KC" )
+        {
+                // KC Header - Can't Decode this
+                return $Makernote_Tag;
+        }
+        if ( substr( $Makernote_Tag['Data'], 0, 8 ) == "+M+M+M+M" )
+        {
+                // +M+M+M+M Header - Can't Decode this
+                return $Makernote_Tag;
+        }
+        else if ( substr( $Makernote_Tag['Data'], 0, 5 ) == "MINOL" )
+        {
+                // MINOL Header - Can't Decode this
+                return $Makernote_Tag;
+        }
+        else
+        {
+                // No Header - Decode the IFD
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Minolta";
+                $Makernote_Tag['Makernote Tags'] = "Olympus";
+
+
+                // Return the new tag
+                return $Makernote_Tag;
+
+        }
+
+
+        // Shouldn't get here
+        return False;
+}
+
+/******************************************************************************
+* End of Function:     get_Minolta_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Minolta_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Olympus_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Minolta_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this Tag uses Olympus type tags - otherwise it cannot be processed here
+        if ( $Tag_Definitions_Name !== "Olympus" )
+        {
+                // Not Olympus Tags - cannot be processed here
+                return FALSE;
+        }
+
+
+        // Process the tag acording to it's tag number, to produce a text value
+
+        if ( ( $Exif_Tag['Tag Number'] == 0x0001 ) ||   // Minolta Camera Settings
+             ( $Exif_Tag['Tag Number'] == 0x0003 ) )
+        {
+
+                // Create the output string
+                $output_str = "";
+
+                // Cycle through each camera setting record which are 4 byte Longs
+
+                for ( $i = 1; $i*4 <= strlen( $Exif_Tag['Data'] ); $i++)
+                {
+                
+                        // Exract the current 4 byte Long value (Motorola byte alignment)
+                        $value = get_IFD_Data_Type( substr($Exif_Tag['Data'], ($i-1)*4, 4) , 4, "MM" );
+
+                        // Corrupt settings can cause huge values, which automatically get
+                        // put into floating point variables instead of integer variables
+                        // Hence Check that this is an integer, as problems will occur if it isn't
+                        if ( is_integer( $value ) )
+                        {
+                        
+                                // Check if the current setting number is in the Definitions array
+                                if ( array_key_exists( $i, ($GLOBALS[ "Minolta_Camera_Setting_Definitions" ]) ) === TRUE )
+                                {
+                                        // Setting is in definitions array
+
+                                        // Get some of the information from the settings definitions array
+                                        $tagname = $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ 'Name' ];
+                                        $units = "";
+                                        if ( array_key_exists( 'Units', $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ] ) )
+                                        {
+                                                $units = $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ 'Units' ];
+                                        }
+                                        // Check what type of field the setting is, and process accordingly
+                                        
+                                        if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Lookup" )
+                                        {
+                                                // This is a lookup table field
+
+                                                // Check if the value read is in the lookup table
+                                                if ( array_key_exists( $value, $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ] ) )
+                                                {
+                                                        // Value is in the lookup table - Add it to the text
+                                                        $output_str .= $tagname . ": " . $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ][ $value ] . "\n";
+                                                }
+                                                else
+                                                {
+                                                        // Value is Not in the lookup table
+                                                        // Add a message if the user has requested to see unknown tags
+                                                        if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                        {
+                                                                $output_str .= $tagname . ": Unknown Reserved Value $value\n";
+                                                        }
+
+                                                }
+                                        }
+                                        else if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Numeric" )
+                                        {
+                                                // This is a numeric type add it as is to the output, with units
+                                                $output_str .= $tagname . ": $value $units\n";
+                                        }
+                                        else if ( $GLOBALS[ "Minolta_Camera_Setting_Definitions" ][ $i ]['Type'] == "Special" )
+                                        {
+                                                // This is a special setting, Process it according to the setting number
+                                                switch ( $i )
+                                                {
+                                                        case 9:         // Apex Film Speed Value
+                                                                $output_str .= $tagname . ": " . ($value/8-1) . " ( ISO " . ((pow(2,($value/8-1)))*3.125) . " )\n";
+                                                                break;
+                                                                
+                                                        case 10:        // Apex Shutter Speed Time Value
+                                                                $output_str .= $tagname . ": " . ($value/8-6);
+                                                                if ( $value == 8 )
+                                                                {
+                                                                        $output_str .=  " ( 30 seconds )\n";
+                                                                }
+                                                                else
+                                                                {
+                                                                        $output_str .=  " ( " . ( pow(2, (48-$value)/8 ) ) . " seconds )\n";
+                                                                }
+                                                                break;
+                                                                
+                                                        case 11:        // Apex Aperture Value
+                                                                $output_str .= $tagname . ": " . ($value/8-1) . " ( F Stop: " . (pow(2,( $value/16-0.5 ))) . " )\n";
+                                                                break;
+                                                                
+                                                        case 14:        // Exposure Compensation
+                                                                $output_str .= $tagname . ": " . ($value/3-2) . " $units\n";
+                                                                break;
+                                                                
+                                                        case 17:        // Interval Length
+                                                                $output_str .= $tagname . ": " . ($value+1) . " $units\n";
+                                                                break;
+                                                                
+                                                        case 19:        // Focal Length
+                                                                $output_str .= $tagname . ": " . ($value/256) . " $units\n";
+                                                                break;
+                                                                
+                                                        case 22:        // Date
+                                                                $output_str .= $tagname . ": " . sprintf( "%d/%d/%d",  ($value%256), floor(($value - floor($value/65536)*65536)/256 ), floor($value/65536) ) . " $units\n";
+                                                                break;
+                                                                
+                                                        case 23:        // Time
+                                                                $output_str .= $tagname . ": " . sprintf( "%2d:%02d:%02d", floor($value/65536), floor(($value - floor($value/65536)*65536)/256 ), ($value%256) ) . " $units\n";
+                                                                break;
+                                                                
+                                                        case 24:        // Max Aperture at this focal length
+                                                                $output_str .= $tagname . ": F" . (pow(2,($value/16-0.5))) ." $units\n";
+                                                                break;
+                                                                
+                                                        case 29:        // White Balance Red
+                                                        case 30:        // White Balance Green
+                                                        case 31:        // White Balance Blue
+                                                                $output_str .= $tagname . ": " . ($value/256) ." $units\n";
+                                                                break;
+                                                                
+                                                        case 32:        // Saturation
+                                                        case 33:        // Contrast
+                                                                $output_str .= $tagname . ": " . ($value-3) ." $units\n";
+                                                                break;
+                                                                
+                                                        case 36:        // Flash Compensation
+                                                                $output_str .= $tagname . ": " . (($value-6)/3) ." $units\n";
+                                                                break;
+                                                                
+                                                        case 42:        // Color Filter
+                                                                $output_str .= $tagname . ": " . ($value-3) ." $units\n";
+                                                                break;
+                                                                
+                                                        case 45:        // Apex Brightness Value
+                                                                $output_str .= $tagname . ": " . ($value/8-6) ." $units\n";
+                                                                break;
+                                                                
+                                                        default:        // Unknown Special Setting
+                                                                // If user has requested to see the unknown tags, then add the setting to the output
+                                                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                                {
+                                                                        $output_str .= "Unknown Special Tag: $tagname, Value: $value $units\n";
+                                                                }
+                                                                break;
+                                                }
+                                        }
+                                        else
+                                        {
+                                                // Unknown Setting Type
+                                                // If user has requested to see the unknown tags, then add the setting to the output
+                                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                {
+                                                        $output_str .= "Unknown Tag Type Tag $i, Value: " . $value . "\n";
+                                                }
+                                        }
+
+
+                                }
+                                else
+                                {
+                                        // Unknown Setting
+                                        // If user has requested to see the unknown tags, then add the setting to the output
+                                        if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                        {
+                                                $output_str .= "Unknown Minolta Camera Setting Tag $i, Value: " . $value . "\n";
+                                        }
+                                }
+                        }
+
+                }
+                
+                // Return the text string
+                return $output_str;
+        }
+        else if ( ( $Exif_Tag['Tag Number'] == 0x0088 ) ||
+                  ( $Exif_Tag['Tag Number'] == 0x0081 ) )
+        {
+                // Konica/Minolta Thumbnail
+                return "Thumbnail";
+        }
+        else
+        {
+                return FALSE;
+        }
+
+}
+
+/******************************************************************************
+* End of Function:     get_Minolta_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Minolta_Camera_Setting_Definitions
+*
+* Contents:     This global variable provides definitions for the fields
+*               contained in the Konica/Minolta Camera Settings Makernote tag,
+*               indexed by their setting number.
+*
+******************************************************************************/
+
+$GLOBALS[ "Minolta_Camera_Setting_Definitions" ] = array(
+
+2 => array ( 'Name' => "Exposure Mode",
+                'Type' => "Lookup",
+                 0 => "P",
+                 1 => "A",
+                 2 => "S",
+                 3 => "M" ),
+
+3 => array (   'Name' => "Flash Mode",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Red-eye reduction",
+                2 => "Rear flash sync",
+                3 => "Wireless" ),
+
+4 => array (   'Name' => "White Balance",
+                'Type' => "Lookup",
+                0 => "Auto",
+                1 => "Daylight",
+                2 => "Cloudy",
+                3 => "Tungsten",
+                5 => "Custom",
+                7 => "Fluorescent",
+                8 => "Fluorescent 2",
+                11 => "Custom 2",
+                12 => "Custom 3" ),
+
+5 => array (   'Name' => "Image Size",
+                'Type' => "Lookup",
+                0 => "2560 x 1920 (2048x1536 - DiMAGE 5 only)",
+                1 => "1600 x 1200",
+                2 => "1280 x 960",
+                3 => "640 x 480" ),
+
+
+6 => array (   'Name' => "Image Quality",
+                'Type' => "Lookup",
+                0 => "Raw",
+                1 => "Super Fine",
+                2 => "Fine",
+                3 => "Standard",
+                4 => "Economy",
+                5 => "Extra Fine" ),
+
+7 => array (   'Name' => "Shooting Mode",
+                'Type' => "Lookup",
+                0 => "Single",
+                1 => "Continuous",
+                2 => "Self-timer",
+                4 => "Bracketing",
+                5 => "Interval",
+                6 => "UHS Continuous",
+                7 => "HS Continuous" ),
+
+
+8 => array (   'Name' => "Metering Mode",
+                'Type' => "Lookup",
+                0 => "Multi-Segment",
+                1 => "Centre Weighted",
+                2 => "Spot" ),
+
+
+9 => array (   'Name' => "Apex Film Speed Value",
+                'Type' => "Special" ),
+
+// 09 FilmSpeed ,  APEX Film Speed Value ,  Speed value = x/8-1 , ISO= (2^(x/8-1))*3.125
+
+
+10 => array (   'Name' => "Apex Shutter Speed Time Value",
+                'Type' => "Special",
+                'Units' => "Seconds?" ),
+
+//  APEX Time Value ,   Time value = x/8-6 ,  ShutterSpeed = 2^( (48-x)/8 ), ! Due to rounding error x=8 should be displayed as 30 sec.
+
+11 => array (   'Name' => "Apex Aperture Value",
+                'Type' => "Special" ),
+
+// APEX Aperture Value   ApertureValue = x/8-1  , Aperture = 2^( x/16-0.5 )
+
+
+12 => array (   'Name' => "Macro Mode",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "On" ),
+
+13 => array (   'Name' => "Digital Zoom",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "Electronic magnification was used",
+                2 => "Digital zoom 2x" ),
+
+
+14 => array (   'Name' => "Exposure Compensation",
+                'Type' => "Special",
+                'Units' => "EV" ),
+
+// EV = x/3 -2  Exposure compensation in EV
+
+
+15 => array (   'Name' => "Bracket Step",
+                'Type' => "Lookup",
+                0 => "1/3 EV",
+                1 => "2/3 EV",
+                2 => "1 EV" ),
+
+
+17 => array (   'Name' => "Interval Length",
+                'Type' => "Special",
+                'Units' => "Min" ),
+
+// interval is x+1 min (used with interval mode)
+
+
+18 => array (   'Name' => "Interval Number",
+                'Type' => "Numeric",
+                'Units' => "frames" ),
+
+19 => array (   'Name' => "Focal Length",
+                'Type' => "Special",
+                'Units' => "mm" ),
+
+//   x / 256 is real focal length in mm  ,  x / 256 * 3.9333 is 35-mm equivalent
+
+
+20 => array (   'Name' => "Focus Distance",
+                'Type' => "Numeric",
+                'Units' => "mm  ( 0 = Infinity)" ),
+
+
+21 => array (   'Name' => "Flash Fired",
+                'Type' => "Lookup",
+                0 => "No",
+                1 => "Yes" ),
+
+22 => array (   'Name' => "Date",
+                'Type' => "Special"  ),
+
+// yyyymmdd ,  year = x/65536 , month = x/256-x/65536*256 , day = x%256
+
+23 => array (   'Name' => "Time",
+                'Type' => "Special"  ),
+
+// hhhhmmss , hour = x/65536 , minute = x/256-x/65536*256 , second = x%256
+
+
+24 => array (   'Name' => "Max Aperture at this focal length",
+                'Type' => "Special"  ),
+
+// Fno = 2^(x/16-0.5)
+
+
+27 => array (   'Name' => "File Number Memory",
+                'Type' => "Lookup",
+                0 => "Off",
+                1 => "On" ),
+
+28 => array (   'Name' => "Last File Number",
+                'Type' => "Numeric",
+                'Units' => "  ( 0 = File Number Memory is Off)" ),
+
+
+29 => array (   'Name' => "White Balance Red",
+                'Type' => "Special"  ),
+
+// x/256 - red white balance coefficient used for this picture
+
+
+30 => array (   'Name' => "White Balance Green",
+                'Type' => "Special"  ),
+
+// x/256 - green white balance coefficient used for this picture
+
+31 => array (   'Name' => "White Balance Blue",
+                'Type' => "Special"  ),
+
+// x/256 - blue white balance coefficient used for this picture
+
+
+32 => array (   'Name' => "Saturation",
+                'Type' => "Special"  ),
+
+//  x-3 = saturation
+
+
+33 => array (   'Name' => "Contrast",
+                'Type' => "Special"  ),
+
+// x-3 - contrast
+
+
+34 => array (   'Name' => "Sharpness",
+                'Type' => "Lookup",
+                0 => "Hard",
+                1 => "Normal",
+                2 => "Soft" ),
+
+
+35 => array (   'Name' => "Subject Program",
+                'Type' => "Lookup",
+                0 => "none",
+                1 => "portrait",
+                2 => "text",
+                3 => "night portrait",
+                4 => "sunset",
+                5 => "sports action" ),
+
+
+36 => array (   'Name' => "Flash Compensation",
+                'Type' => "Special",
+                'Units' => "EV"  ),
+
+//  (x-6)/3 = flash compensation in EV
+
+
+37 => array (   'Name' => "ISO Setting",
+                'Type' => "Lookup",
+                0 => "100",
+                1 => "200",
+                2 => "400",
+                3 => "800",
+                4 => "auto",
+                5 => "64" ),
+
+
+38 => array (   'Name' => "Camera Model",
+                'Type' => "Lookup",
+                0 => "DiMAGE 7",
+                1 => "DiMAGE 5",
+                2 => "DiMAGE S304",
+                3 => "DiMAGE S404",
+                4 => "DiMAGE 7i",
+                5 => "DiMAGE 7Hi",
+                6 => "DiMAGE A1",
+                7 => "DiMAGE S414" ),
+
+
+39 => array (   'Name' => "Interval Mode",
+                'Type' => "Lookup",
+                0 => "Still Image",
+                1 => "Time-lapse Movie" ),
+
+
+40 => array (   'Name' => "Folder Name",
+                'Type' => "Lookup",
+                0 => "Standard Form",
+                1 => "Data Form" ),
+
+
+41 => array (   'Name' => "Color Mode",
+                'Type' => "Lookup",
+                0 => "Natural Color",
+                1 => "Black & White",
+                2 => "Vivid Color",
+                3 => "Solarization",
+                4 => "Adobe RGB" ),
+
+
+42 => array (   'Name' => "Color Filter",
+                'Type' => "Special" ),
+
+// x-3 = color filter
+
+
+43 => array (   'Name' => "Black & White Filter",
+                'Type' => "Numeric" ),
+
+
+
+44 => array (   'Name' => "Internal Flash",
+                'Type' => "Lookup",
+                0 => "Not Fired",
+                1 => "Fired" ),
+
+
+
+45 => array (   'Name' => "Apex Brightness Value",
+                'Type' => "Special" ),
+
+// Brightness Value = x/8-6
+
+
+
+
+46 => array (   'Name' => "Spot Focus Point X Coordinate",
+                'Type' => "Numeric" ),
+
+
+
+47 => array (   'Name' => "Spot Focus Point Y Coordinate",
+                'Type' => "Numeric" ),
+
+
+
+48 => array (   'Name' => "Wide Focus Zone",
+                'Type' => "Lookup",
+                0 => "No Zone or AF Failed",
+                1 => "Center Zone (Horizontal Orientation)",
+                2 => "Center Zone (Vertical Orientation)",
+                3 => "Left Zone",
+                4 => "Right Zone" ),
+
+
+49 => array (   'Name' => "Focus Mode",
+                'Type' => "Lookup",
+                0 => "Auto Focus",
+                1 => "Manual Focus" ),
+
+
+50 => array (   'Name' => "Focus Area",
+                'Type' => "Lookup",
+                0 => "Wide Focus (normal)",
+                1 => "Spot Focus" ),
+
+
+51 => array (   'Name' => "DEC Switch Position",
+                'Type' => "Lookup",
+                0 => "Exposure",
+                1 => "Contrast",
+                2 => "Saturation",
+                3 => "Filter" ),
+
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     Minolta_Camera_Setting_Definitions
+******************************************************************************/
+
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/epson.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/epson.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/epson.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/epson.php	2004/08/02 10:33:32
@@ -0,0 +1,119 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     epson.php
+*
+* Description:  Epson Makernote Parser
+*               Provides functions to decode an Epson EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Epson Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          8 Bytes         "EPSON\x00\x01\x00"
+*               IFD Data        Variable        Standard IFD Data using Olympus Tags
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Epson makernote uses Olympus tags - ensure they are included
+
+include_once 'olympus.php';
+
+
+
+
+// Add the Parser function to the list of Makernote Parsers. (Interpreter Functions are supplied by the Olympus script)
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Epson_Makernote";
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Epson_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Epson_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Epson
+        if ( stristr( $Make_Field, "Epson" ) === FALSE )
+        {
+                return FALSE;
+        }
+        
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 8 ) != "EPSON\x00\x01\x00" )
+        {
+                // This isn't a Epson Makernote, abort
+                return FALSE ;
+        }
+
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 );
+
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Epson";
+        $Makernote_Tag['Makernote Tags'] = "Olympus";
+
+
+        // Return the new tag
+        return $Makernote_Tag;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Epson_Makernote
+******************************************************************************/
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/kyocera.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/kyocera.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/kyocera.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/kyocera.php	2004/08/06 22:08:06
@@ -0,0 +1,241 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     kyocera.php
+*
+* Description:  Kyocera Makernote Parser
+*               Provides functions to decode an Kyocera EXIF makernote and to interpret
+*               the resulting array into html. Includes Kyocera's Contax brand
+*
+*               Kyocera Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          22 Bytes        "KYOCERA            \x00\x00\x00"
+*               IFD Data        Variable        NON-Standard IFD Data using Kyocera Tags
+*                                               IFD has no Next-IFD pointer at end of IFD,
+*                                               and Offsets are relative to the start
+*                                               of the current IFD tag, not the TIFF header
+*               ----------------------------------------------------------------
+*
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Kyocera_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Kyocera_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Kyocera_Makernote_Html";
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Kyocera_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Kyocera_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Contax or Kyocera
+        if ( ( stristr( $Make_Field, "Contax" ) === FALSE ) &&
+             ( stristr( $Make_Field, "Kyocera" ) === FALSE ) )
+        {
+                // Kyocera or Contax not found in maker field - abort
+                return FALSE;
+        }
+
+
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 22 ) != "KYOCERA            \x00\x00\x00" )
+        {
+                // This isn't a Kyocera Makernote, abort
+                return FALSE ;
+        }
+
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 22 );
+
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Kyocera", True, False );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Kyocera";
+        $Makernote_Tag['Makernote Tags'] = "Kyocera";
+
+
+        // Return the new tag
+        return $Makernote_Tag;
+}
+
+/******************************************************************************
+* End of Function:     get_Kyocera_Makernote
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Kyocera_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Kyocera_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Kyocera_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this tag uses Kyocera tags, otherwise it can't be interpreted here
+        if ( $Tag_Definitions_Name == "Kyocera" )
+        {
+                // No Special Kyocera tags so far
+                return FALSE;
+        }
+
+        return FALSE;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Kyocera_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Kyocera_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Kyocera_Makernote_Html( $Makernote_tag, $filename )
+{
+
+        // Check that this is a Kyocera Makernote, otherwise it can't be interpreted here
+        if ( $Makernote_tag['Makernote Type'] != "Kyocera" )
+        {
+                // Not a Kyocera Makernote - cannot interpret it - abort
+                return False;
+        }
+
+        // Interpret the IFD and return the HTML
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Kyocera_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Kyocera
+*
+* Contents:     This global variable provides definitions of the known Kyocera
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Kyocera"] = array(
+
+1 => array(             'Name' => "Kyocera Proprietory Format Thumbnail",
+                        'Type' => "Unknown" ),
+
+0x0E00 => array(        'Name' => "Print Image Matching Info",
+                        'Type' => "PIM" ),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Kyocera
+******************************************************************************/
+
+
+
+
+
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/Pentax.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/Pentax.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/Pentax.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/Pentax.php	2004/08/04 16:10:22
@@ -0,0 +1,353 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     pentax.php
+*
+* Description:  Pentax (Asahi) Makernote Parser
+*               Provides functions to decode an Pentax (Asahi) EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Pentax Makernote Format:
+*
+*               Type 1
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               IFD Data        Variable        NON-Standard IFD Data using Pentax Tags
+*                                               IFD has no Next-IFD pointer at end of IFD,
+*                                               and Offsets are relative to the start
+*                                               of the current IFD tag, not the TIFF header
+*               ----------------------------------------------------------------
+*
+*
+*               Type 2
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          4 Bytes         "AOC\x00"
+*               Unknown         2 Bytes         Unknown field
+*               IFD Data        Variable        NON-Standard IFD Data using Casio Type 2 Tags
+*                                               IFD has no Next-IFD pointer at end of IFD,
+*                                               and Offsets are relative to the start
+*                                               of the current IFD tag, not the TIFF header
+*               ----------------------------------------------------------------
+*
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Pentax Type 2 makernote uses Casio Type 2 tags - ensure they are included
+
+include_once 'casio.php';
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Pentax_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Pentax_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Pentax_Makernote_Html";
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Pentax_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Pentax_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Pentax or Asahi
+        if ( ( stristr( $Make_Field, "Pentax" ) === FALSE ) &&
+             ( stristr( $Make_Field, "Asahi" ) === FALSE ) )
+        {
+                // Couldn't find Pentax or Asahi in the maker - abort
+                return FALSE;
+        }
+
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'], 0, 4 ) == "AOC\x00" )
+        {
+                // Type 2 Pentax Makernote
+                
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 6 );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Casio Type 2" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Casio Type 2";
+                $Makernote_Tag['Makernote Tags'] = "Casio Type 2";
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+        else
+        {
+                // Type 1 Penax Makernote
+                
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Pentax" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Pentax";
+                $Makernote_Tag['Makernote Tags'] = "Pentax";
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+
+
+        // Shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Pentax_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Pentax_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Pentax_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Pentax_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this tag uses the Pentax tags, otherwise it can't be interpreted here
+        if ( $Tag_Definitions_Name == "Pentax" )
+        {
+                // No Special Tags so far
+                return FALSE;
+        }
+
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Pentax_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Pentax_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Pentax_Makernote_Html( $Makernote_tag, $filename )
+{
+        // Check that this is a Pentax type makernote
+        if ( $Makernote_tag['Makernote Type'] != "Pentax" )
+        {
+                // Not a Pentax makernote - abort
+                return False;
+        }
+
+        // Interpret the IFD and return the html
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Pentax_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Pentax
+*
+* Contents:     This global variable provides definitions of the known Pentax Type 1
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Pentax"] = array(
+
+
+0x0001 => array(        'Name' => "Capture Mode",
+                        'Type' => "Lookup",
+                        0 => "Auto",
+                        1 => "Night-scene",
+                        2 => "Manual",
+                        4 => "Multiple" ),
+
+0x0002 => array(        'Name' => "Quality Level",
+                        'Type' => "Lookup",
+                        0 => "Good",
+                        1 => "Better",
+                        2 => "Best" ),
+
+0x0003 => array(        'Name' => "Focus Mode",
+                        'Type' => "Lookup",
+                        2 => "Custom",
+                        3 => "Auto" ),
+
+0x0004 => array(        'Name' => "Flash Mode",
+                        'Type' => "Lookup",
+                        1 => "Auto",
+                        2 => "Flash on",
+                        4 => "Flash off",
+                        6 => "Red-eye Reduction" ),
+
+0x0007 => array(        'Name' => "White Balance",
+                        'Type' => "Lookup",
+                        0 => "Auto",
+                        1 => "Daylight",
+                        2 => "Shade",
+                        3 => "Tungsten",
+                        4 => "Fluorescent",
+                        5 => "Manual" ),
+
+
+0x000a => array(        'Name' => "Digital Zoom",
+                        'Type' => "Numeric",
+                        'Units' => "  (0 = Off)" ),
+
+0x000b => array(        'Name' => "Sharpness",
+                        'Type' => "Lookup",
+                        0 => "Normal",
+                        1 => "Soft",
+                        2 => "Hard" ),
+
+0x000c => array(        'Name' => "Contrast",
+                        'Type' => "Lookup",
+                        0 => "Normal",
+                        1 => "Low",
+                        2 => "High" ),
+
+0x000d => array(        'Name' => "Saturation",
+                        'Type' => "Lookup",
+                        0 => "Normal",
+                        1 => "Low",
+                        2 => "High" ),
+
+0x0014 => array(        'Name' => "ISO Speed",
+                        'Type' => "Lookup",
+                        10 => "100",
+                        16 => "200",
+                        100 => "100",
+                        200 => "200" ),
+
+0x0017 => array(        'Name' => "Colour",
+                        'Type' => "Lookup",
+                        1 => "Normal",
+                        2 => "Black & White",
+                        3 => "Sepia" ),
+
+0x0e00 => array(        'Name' => "Print Image Matching Info",
+                        'Type' => "PIM" ),
+
+0x1000 => array(        'Name' => "Time Zone",
+                        'Type' => "String" ),
+
+0x1001 => array(        'Name' => "Daylight Savings",
+                        'Type' => "String" ),
+
+
+
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Pentax
+******************************************************************************/
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/olympus.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/olympus.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/olympus.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/olympus.php	2005/01/20 22:53:18
@@ -0,0 +1,486 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     olympus.php
+*
+* Description:  Olympus Makernote Parser
+*               Provides functions to decode an Olympus EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Olympus Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          7 Bytes         "OLYMP\x00\x01" or "OLYMP\x00\x02"
+*               Unknown         1 Bytes         Unknown
+*               IFD Data        Variable        Standard IFD Data using Olympus Tags
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.11 : changed get_Olympus_Makernote_Html to allow thumbnail links to work when
+*                              toolkit is portable across directories
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Olympus_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Olympus_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Olympus_Makernote_Html";
+
+
+
+
+include_once dirname(__FILE__) .'/../pjmt_utils.php';          // Change: as of version 1.11 - added to allow directory portability
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Olympus_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Olympus_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+
+        // Check if the Make Field contains the word Olympus
+        if ( stristr( $Make_Field, "Olympus" ) === FALSE )
+        {
+                return FALSE;
+        }
+
+        // Check if the header exists at the start of the Makernote
+        if ( ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x01" ) &&
+             ( substr( $Makernote_Tag['Data'], 0, 7 ) != "OLYMP\x00\x02" ) )
+        {
+                // This isn't a Olympus Makernote, abort
+                return FALSE ;
+        }
+
+
+
+        // Seek to the start of the IFD
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 );
+
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Olympus" );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Olympus";
+        $Makernote_Tag['Makernote Tags'] = "Olympus";
+
+
+        // Return the new tag
+        return $Makernote_Tag;
+}
+
+/******************************************************************************
+* End of Function:     get_Olympus_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Olympus_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Olympus_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Olympus_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+        // Check that this tag uses the Olympus tags, otherwise it can't be decoded here
+        if ( $Tag_Definitions_Name !== "Olympus" )
+        {
+                // Not an Olympus tag - can't decode it
+                return FALSE;
+        }
+
+
+        // Process the tag acording to it's tag number, to produce a text value
+        if ( $Exif_Tag['Tag Number'] == 0x200 )
+        {
+                // Special Mode Tag
+
+                // Add info from the first value to the output string
+                switch ( $Exif_Tag['Data'][0] )
+                {
+                        case 0: $outputstr = "Normal\n";
+                                break;
+                        case 2: $outputstr = "Fast\n";
+                                break;
+                        case 3: $outputstr = "Panorama\n";
+                                break;
+                        default: $outputstr = "Unknown Mode ( " . $Exif_Tag['Data'][0] . " )\n";
+                                        break;
+                }
+
+                // Add info from the second value to the output string
+                $outputstr .= "Sequence Number: " . $Exif_Tag['Data'][1] . "\n";
+
+                // Add info from the third value to the output string
+                switch ( $Exif_Tag['Data'][2] )
+                {
+                        case 0: // Do nothing
+                                break;
+                        case 1: $outputstr .= "Panorama Direction: Left to Right\n";
+                                break;
+                        case 2: $outputstr .= "Panorama Direction: Right to Left\n";
+                                break;
+                        case 3: $outputstr .= "Panorama Direction: Bottom to Top\n";
+                                break;
+                        case 4: $outputstr .= "Panorama Direction: Top to Bottom\n";
+                                break;
+                        default: $outputstr .= "Unknown Panorama Direction\n";
+                                        break;
+                }
+
+                // Return the output string
+                return $outputstr;
+        }
+        else
+        {
+                // Unknown special tag - can't process it here
+                return FALSE;
+        }
+
+        // Unknown special tag - can't process it here
+        return FALSE;
+
+}
+
+/******************************************************************************
+* End of Function:     get_Olympus_Text_Value
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Olympus_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Olympus_Makernote_Html( $Makernote_tag, $filename )
+{
+
+        // Check that this tag uses the Olympus tags, otherwise it can't be interpreted here
+        if ( $Makernote_tag['Makernote Tags'] != "Olympus" )
+        {
+                // Not Olympus tags - can't interpret with this function
+                return FALSE;
+        }
+
+        // Check if the Decoded data is valid
+        if ( $Makernote_tag['Decoded Data'][0] === FALSE )
+        {
+                // Decoded data is not valid - can't interpret with this function
+                return FALSE;
+        }
+
+        // Minolta Thumbnail 1
+        if ( ( array_key_exists( 0x0088, $Makernote_tag['Decoded Data'][0] ) ) &&
+             ( $Makernote_tag['Makernote Tags'] == "Olympus" ) )
+        {
+                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                // Build the path of the thumbnail script and its filename parameter to put in a url
+                $link_str = get_relative_path( dirname(__FILE__)  . "/../get_minolta_thumb.php" , getcwd ( ) );
+                $link_str .= "?filename=";
+                $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." );
+
+                // Add thumbnail link to html
+                $Makernote_tag['Decoded Data'][0][0x0088]['Text Value'] = "<a class=\"EXIF_Minolta_Thumb_Link\" href=\"$link_str\" ><img class=\"EXIF_Minolta_Thumb\" src=\"$link_str\"></a>";
+
+                $Makernote_tag['Decoded Data'][0][0x0088]['Type'] = "String";
+        }
+        // Minolta Thumbnail 2
+        if ( ( array_key_exists( 0x0081, $Makernote_tag['Decoded Data'][0] ) ) &&
+             ( $Makernote_tag['Makernote Tags'] == "Olympus" ) )
+        {
+                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                // Build the path of the thumbnail script and its filename parameter to put in a url
+                $link_str = get_relative_path( dirname(__FILE__) . " /../get_minolta_thumb.php" , getcwd ( ) );
+                $link_str .= "?filename=";
+                $link_str .= get_relative_path( $filename, dirname(__FILE__) ."/.." );
+
+                // Add thumbnail link to html
+                $Makernote_tag['Decoded Data'][0][0x0081]['Text Value'] = "<a class=\"EXIF_Minolta_Thumb_Link\" href=\"$link_str\" ><img class=\"EXIF_Minolta_Thumb\" src=\"$link_str\"></a>";
+                $Makernote_tag['Decoded Data'][0][0x0081]['Type'] = "String";
+        }
+
+        // Interpret the IFD and return the HTML
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Olympus_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Olympus
+*
+* Contents:     This global variable provides definitions of the known Olympus
+*               Makernote tags, indexed by their tag number.
+*               It also includes Minolta and Agfa tags, as they use many of the
+*               same tags
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Olympus"] = array(
+
+0x0000 => array( 'Name' => "Makernote Version",           // Minolta
+                 'Type' => "String" ),
+
+0x0001 => array( 'Name' => "Camera Settings",           // Minolta
+                 'Type' => "Special" ),
+
+0x0003 => array( 'Name' => "Camera Settings",           // Minolta
+                 'Type' => "Special" ),
+
+0x0040 => array( 'Name' => "Compressed Image Size",           // Minolta
+                 'Type' => "Numeric",
+                 'Units' => "Bytes" ),
+
+0x0081 => array( 'Name' => "Minolta Thumbnail",           // Minolta
+                 'Type' => "Special" ),
+
+0x0088 => array( 'Name' => "Minolta Thumbnail",           // Minolta
+                 'Type' => "Special" ),
+
+0x0089 => array( 'Name' => "Minolta Thumbnail Length",           // Minolta
+                 'Type' => "Numeric",
+                 'Units' => "bytes" ),
+
+0x0101 => array( 'Name' => "Colour Mode",           // Minolta
+                 'Type' => "Lookup",
+                 0 => "Natural Colour",
+                 1 => "Black & White",
+                 2 => "Vivid colour",
+                 3 => "Solarization",
+                 4 => "AdobeRGB" ),
+
+0x0102 => array( 'Name' => "Image Quality",           // Minolta
+                 'Type' => "Lookup",
+                 0 => "Raw",
+                 1 => "Super Fine",
+                 2 => "Fine",
+                 3 => "Standard",
+                 4 => "Extra Fine" ),
+
+0x0103 => array( 'Name' => "Image Quality?",           // Minolta
+                 'Type' => "Lookup",
+                 0 => "Raw",
+                 1 => "Super Fine",
+                 2 => "Fine",
+                 3 => "Standard",
+                 4 => "Extra Fine" ),
+
+
+
+0x0200 => array( 'Name' => "Special Mode",
+                'Type' => "Special" ),
+
+
+0x0201 => array( 'Name' => "JPEG Quality",
+                 'Type' => "Lookup",
+                 1 => "Standard Quality",
+                 2 => "High Quality",
+                 3 => "Super High Quality" ),
+
+0x0202 => array( 'Name' => "Macro",
+                 'Type' => "Lookup",
+                 0 => "Normal (Not Macro)",
+                 1 => "Macro" ),
+
+0x0204 => array( 'Name' => "Digital Zoom",
+                 'Type' => "Numeric",
+                 'Units' => " x Digital Zoom, (0 or 1 = normal)" ),
+
+0x0207 => array( 'Name' => "Firmware Version",
+                'Type' => "String" ),
+
+
+0x0208 => array( 'Name' => "Picture Info Data",
+                'Type' => "String" ),
+
+0x0209 => array( 'Name' => "Camera ID",
+                'Type' => "String" ),
+
+
+0x020B => array( 'Name' => "Image Width",        // Epson Tag
+                'Type' => "Pixels" ),
+
+0x020C => array( 'Name' => "Image Height",        // Epson Tag
+                'Type' => "Pixels" ),
+
+0x020D => array( 'Name' => "Original Manufacturer Model?",        // Epson Tag
+                'Type' => "String" ),
+
+0x0E00 => array( 'Name'=> "Print Image Matching Info",     // Minolta Tag
+                 'Type' => "PIM" ),
+
+0x1004 => array( 'Name' => "Flash Mode",
+                 'Type' => "Numeric" ),
+
+0x1006 => array( 'Name' => "Bracket",
+                 'Type' => "Numeric" ),
+
+0x100B => array( 'Name' => "Focus Mode",
+                 'Type' => "Numeric" ),
+
+0x100C => array( 'Name' => "Focus Distance",
+                 'Type' => "Numeric" ),
+
+0x100D => array( 'Name' => "Zoom",
+                 'Type' => "Numeric" ),
+
+0x100E => array( 'Name' => "Macro Focus",
+                 'Type' => "Numeric" ),
+
+0x100F => array( 'Name' => "Sharpness",
+                 'Type' => "Numeric" ),
+
+0x1011 => array( 'Name' => "Colour Matrix",
+                 'Type' => "Numeric" ),
+
+0x1012 => array( 'Name' => "Black Level",
+                 'Type' => "Numeric" ),
+
+0x1015 => array( 'Name' => "White Balance",
+                 'Type' => "Numeric" ),
+
+0x1017 => array( 'Name' => "Red Bias",
+                 'Type' => "Numeric" ),
+
+0x1018 => array( 'Name' => "Blue Bias",
+                 'Type' => "Numeric" ),
+
+0x101A => array( 'Name' => "Serial Number",
+                 'Type' => "Numeric" ),
+
+0x1023 => array( 'Name' => "Flash Bias",
+                 'Type' => "Numeric" ),
+
+0x1029 => array( 'Name' => "Contrast",
+                 'Type' => "Numeric" ),
+
+0x102A => array( 'Name' => "Sharpness Factor",
+                 'Type' => "Numeric" ),
+
+0x102B => array( 'Name' => "Colour Control",
+                 'Type' => "Numeric" ),
+
+0x102C => array( 'Name' => "Valid Bits",
+                 'Type' => "Numeric" ),
+
+0x102D => array( 'Name' => "Coring Filter",
+                 'Type' => "Numeric" ),
+
+0x102E => array( 'Name' => "Final Width",
+                 'Type' => "Numeric" ),
+
+0x102F => array( 'Name' => "Final Height",
+                 'Type' => "Numeric" ),
+
+0x1034 => array( 'Name' => "Compression Ratio",
+                 'Type' => "Numeric" ),
+
+
+
+);
+
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Olympus
+******************************************************************************/
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/nikon.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/nikon.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/nikon.php	2011/08/17 20:12:04	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/nikon.php	2004/08/06 22:36:18
@@ -0,0 +1,731 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     Nikon.php
+*
+* Description:  Nikon Makernote Parser
+*               Provides functions to decode an Nikon EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Nikon Makernote Format:
+*
+*               Type 1
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          8 Bytes         "Nikon\x00\x01\x00"
+*               IFD Data        Variable        Standard IFD Data using Nikon Type 1 Tags
+*               ----------------------------------------------------------------
+*
+*               Type 2
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               IFD Data        Variable        Standard IFD Data using Nikon Type 3 Tags
+*               ----------------------------------------------------------------
+*
+*               Type 3
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               Header          10 Bytes         "Nikon\x00\x02\x10\x00\x00"
+*                                               or
+*                                               "Nikon\x00\x02\x00\x00\x00"
+*               TIFF Data       Variable        TIFF header, with associated
+*                                               Standard IFD Data using, Nikon
+*                                               Type 3 Tags. Offsets are from
+*                                               this second tiff header
+*               ----------------------------------------------------------------
+*
+*               // Note: The Nikon Coolpix 775 uses the Fujifilm makernote format
+*
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Nikon_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Nikon_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Nikon_Makernote_Html";
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Nikon_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Nikon_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Nikon
+        if ( stristr( $Make_Field, "Nikon" ) === FALSE )
+        {
+                // Nikon not found in maker field - abort
+                return FALSE;
+        }
+
+
+        // Check if the header exists at the start of the Makernote
+        if ( substr( $Makernote_Tag['Data'],0 , 8 ) == "Nikon\x00\x01\x00" )
+        {
+                // Nikon Type 1 Makernote
+                
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 8 );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Nikon Type 1" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Nikon Type 1";
+                $Makernote_Tag['Makernote Tags'] = "Nikon Type 1";
+
+
+                // Return the new tag
+                return $Makernote_Tag;
+        
+
+        }
+        else if ( ( substr( $Makernote_Tag['Data'],0 , 10 ) == "Nikon\x00\x02\x10\x00\x00" ) ||
+                  ( substr( $Makernote_Tag['Data'],0 , 10 ) == "Nikon\x00\x02\x00\x00\x00" ) )
+        {
+                // Nikon Type 3 Makernote
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 10 );
+
+                // Read the TIFF header and IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = process_TIFF_Header( $filehnd, "Nikon Type 3" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Makernote Type'] = "Nikon Type 3";
+                $Makernote_Tag['Makernote Tags'] = "Nikon Type 3";
+                $Makernote_Tag['Decoded'] = TRUE;
+                
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+        else if ( substr( $Makernote_Tag['Data'],0 , 8 ) == "FUJIFILM" )
+        {
+                // Fuji Makernote - used by Nikon Coolpix 775
+                // Let the Fujifilm library handle it
+                return False;
+        }
+        else
+        {
+                // No header - Nikon Type 2
+
+                // Seek to the start of the IFD
+                fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset'] + 0 );
+
+                // Read the IFD(s) into an array
+                $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Nikon Type 3" );
+
+                // Save some information into the Tag element to aid interpretation
+                $Makernote_Tag['Decoded'] = TRUE;
+                $Makernote_Tag['Makernote Type'] = "Nikon Type 2";
+                $Makernote_Tag['Makernote Tags'] = "Nikon Type 3";
+
+
+                // Return the new tag
+                return $Makernote_Tag;
+        }
+
+
+        // Shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Nikon_Makernote
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Nikon_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Nikon_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Nikon_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+
+        // Check that this tag uses the Nikon tags, otherwise it can't be interpreted here
+        // And check which variety of tags
+        if ( $Tag_Definitions_Name == "Nikon Type 1" )
+        {
+                // No special tags for Nikon type 1 so far
+                return FALSE;
+        }
+        else if ( $Tag_Definitions_Name == "Nikon Type 3" )
+        {
+                // Nikon Type 3 special tag
+                
+                // Process tag according to it's tag number
+                
+                if ( $Exif_Tag['Tag Number'] == 1)      // Nikon Makernote Version - some are binary, some are text
+                {
+                        return "\"" .HTML_UTF8_Escape( $Exif_Tag['Data'] ) . "\" (" . bin2hex( $Exif_Tag['Data'] ) . " hex)";
+                }
+
+                else if ( ( $Exif_Tag['Tag Number'] == 2  ) ||   // ISO Speed Used
+                          ( $Exif_Tag['Tag Number'] == 19 ) )    // ISO Speed Requested
+                {
+                        // ISO speed settings - should be the second of two values
+                        if ( count( $Exif_Tag['Data'] ) == 2 )
+                        {
+                                // There are two values - display the second
+                                return $Exif_Tag['Data'][1] . " " . $Exif_Tag['Units'];
+                        }
+                        else
+                        {
+                                // There is not two values - display generic version of values
+                                return get_IFD_value_as_text( $Exif_Tag['Data'] )  . " " . $Exif_Tag['Units'];
+                        }
+                }
+                else if ( $Exif_Tag['Tag Number'] == 137  )     // Bracketing & Shooting Mode
+                {
+                        // Add shooting mode to output from first two bits
+                        switch ( $Exif_Tag['Data'][0] & 0x03 )
+                        {
+                                case 0x00:
+                                        $outputstr = "Shooting Mode: Single Frame\n";
+                                        break;
+                                case 0x01:
+                                        $outputstr = "Shooting Mode: Continuous\n";
+                                        break;
+                                case 0x02:
+                                        $outputstr = "Shooting Mode: Self Timer\n";
+                                        break;
+                                case 0x03:
+                                        $outputstr = "Shooting Mode: Remote??\n";
+                                        break;
+                                default:
+                                        $outputstr = "Shooting Mode: Unknown\n";
+                                        break;
+                        }
+
+                        // Add flash bracketing to output from fifth bit
+                        if ( ( $Exif_Tag['Data'][0] & 0x10 ) == 0x10 )
+                        {
+                                $outputstr .= "AE/Flash Bracketing On\n";
+                        }
+                        else
+                        {
+                                $outputstr .= "AE/Flash Bracketing Off\n";
+                        }
+                        
+                        // Add white balance bracketing to output from seventh bit
+                        if ( ( $Exif_Tag['Data'][0] & 0x40 ) == 0x40 )
+                        {
+                                $outputstr .= "White Balance Bracketing On\n";
+                        }
+                        else
+                        {
+                                $outputstr .= "White Balance Bracketing Off\n";
+                        }
+                        
+                        // Return the output
+                        return $outputstr;
+
+                }
+                else if ( $Exif_Tag['Tag Number'] == 136  )     // Auto Focus Area
+                {
+                        // Create a string to receive the output
+                        $outputstr = "";
+                        
+                        // If all zeros, this could be manual focus
+                        if ( $Exif_Tag['Data'] == "\x00\x00\x00\x00" )
+                        {
+                                $outputstr .= "Manual Focus, or\n";
+                        }
+
+                        // Add AF mode according to the first byte
+                        switch ( ord($Exif_Tag['Data']{0}) )
+                        {
+                                case 0x00:
+                                        $outputstr .= "Auto Focus Mode: Single Area\n";
+                                        break;
+                                case 0x01:
+                                        $outputstr .= "Auto Focus Mode: Dynamic Area\n";
+                                        break;
+                                case 0x02:
+                                        $outputstr .= "Auto Focus Mode: Closest Subject\n";
+                                        break;
+                                default:
+                                        $outputstr .= "Auto Focus Mode: Unknown AF Mode\n";
+                                        break;
+                        }
+
+                        // Add AF area according to second byte
+                        switch ( ord($Exif_Tag['Data']{1}) )
+                        {
+                                case 0x00:
+                                        $outputstr .= "Auto Focus Area Selected: Centre\n";
+                                        break;
+                                case 0x01:
+                                        $outputstr .= "Auto Focus Area Selected: Top\n";
+                                        break;
+                                case 0x02:
+                                        $outputstr .= "Auto Focus Area Selected: Bottom\n";
+                                        break;
+                                case 0x03:
+                                        $outputstr .= "Auto Focus Area Selected: Left\n";
+                                        break;
+                                case 0x04:
+                                        $outputstr .= "Auto Focus Area Selected: Right\n";
+                                        break;
+                        }
+                        
+                        // Add properly focused areas to output according to byte 3 bits
+                        
+                        $outputstr .= "Properly Focused Area(s): ";
+                        if ( ord($Exif_Tag['Data']{3}) == 0x00 )
+                        {
+                                $outputstr .= "None";
+                        }
+                        if ( ( ord($Exif_Tag['Data']{3}) & 0x01 ) == 0x01 )
+                        {
+                                $outputstr .= "Centre ";
+                        }
+                        if ( ( ord($Exif_Tag['Data']{3}) & 0x02 ) == 0x02 )
+                        {
+                                $outputstr .= "Top ";
+                        }
+                        if ( ( ord($Exif_Tag['Data']{3}) & 0x04 ) == 0x04 )
+                        {
+                                $outputstr .= "Bottom ";
+                        }
+                        if ( ( ord($Exif_Tag['Data']{3}) & 0x08 ) == 0x08 )
+                        {
+                                $outputstr .= "Left ";
+                        }
+                        if ( ( ord($Exif_Tag['Data']{3}) & 0x10 ) == 0x10 )
+                        {
+                                $outputstr .= "Right ";
+                        }
+                        $outputstr .= "\n";
+
+                        // return the string
+                        return $outputstr;
+                }
+                else
+                {
+                        // Unknown special tag
+                        return FALSE;
+                }
+        }
+
+        
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Nikon_Text_Value
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Nikon_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Nikon_Makernote_Html( $Makernote_tag, $filename )
+{
+
+        // Check that this is a Nikon Makernote, otherwise it can't be interpreted here
+        if ( ( $Makernote_tag['Makernote Type'] != "Nikon Type 1" ) &&
+             ( $Makernote_tag['Makernote Type'] != "Nikon Type 2" ) &&
+             ( $Makernote_tag['Makernote Type'] != "Nikon Type 3" ) )
+        {
+                // Not a Nikon Makernote - cannot interpret it - abort
+                return FALSE;;
+        }
+
+        // Interpret the IFD and return the HTML
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+}
+
+/******************************************************************************
+* End of Function:     get_Nikon_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Nikon Type 1
+*
+* Contents:     This global variable provides definitions of the known Nikon Type 1
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 1"] = array(
+
+
+3 => array(     'Name' => "Quality",
+                'Description' => "1: VGA Basic, 2: VGA Normal, 3: VGA Fine, 4: SXGA Basic, 5: SXGA Normal, 6: SXGA Fine",
+                'Type' => "Lookup",
+                1 => "VGA (640x480) Basic",
+                2 => "VGA (640x480) Normal",
+                3 => "VGA (640x480) Fine",
+                4 => "SXGA (1280x960) Basic",
+                5 => "SXGA (1280x960) Normal",
+                6 => "SXGA (1280x960) Fine",
+                7 => "Unknown, Possibly XGA (1024x768) Basic",
+                8 => "Unknown, Possibly XGA (1024x768) Basic",
+                9 => "Unknown, Possibly XGA (1024x768) Basic",
+                10 => "UXGA (1600x1200) Basic",
+                11 => "UXGA (1600x1200) Normal",
+                12 => "UXGA (1600x1200) Fine" ),
+
+4 => array(     'Name' => "Colour Mode",
+                'Description' => "1: Colour, 2: Monochrome.",
+                'Type' => "Lookup",
+                1 => "Colour",
+                2 => "Monochrome" ),
+
+5 => array(     'Name' => "Image Adjustment",
+                'Description' => "0: Normal, 1: Bright+, 2: Bright-, 3: Contrast+, 4: Contrast-.",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Bright+",
+                2 => "Bright-",
+                3 => "Contrast+",
+                4 => "Contrast-" ),
+
+6 => array(     'Name' => "CCD Sensitivity",
+                'Description' => "0: ISO80, 2: ISO160, 4: ISO320, 5: ISO100",
+                'Type' => "Lookup",
+                0 => "ISO 80",
+                2 => "ISO 160",
+                4 => "ISO 320",
+                5 => "ISO 100" ),
+
+7 => array(     'Name' => "White Balance",
+                'Description' => "0: Auto, 1: Preset, 2: Daylight, 3: Incandescense, 4: Fluorescence, 5: Cloudy, 6: SpeedLight",
+                'Type' => "Lookup",
+                0 => "Auto",
+                1 => "Preset",
+                2 => "Daylight",
+                3 => "Incandescense",
+                4 => "Flourescence",
+                5 => "Cloudy",
+                6 => "Speedlight" ),
+
+8 => array(     'Name' => "Focus",
+                'Description' => "If infinite focus, value is '1/0'.",
+                'Type' => "Numeric" ),
+
+10 => array(    'Name' => "Digital Zoom",
+                'Description' => "'160/100' means 1.6x digital zoom, '0/100' means no digital zoom (optical zoom only).",
+                'Type' => "Numeric" ),
+
+11 => array(    'Name' => "Converter",
+                'Description' => "If Fisheye Converter is used, value is 1",
+                'Type' => "Lookup",
+                0 => "No Converter Used",
+                1 => "Fish-eye Converter Used" )
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Nikon Type 1
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Nikon Type 3
+*
+* Contents:     This global variable provides definitions of the known Nikon Type 3
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]["Nikon Type 3"] = array(
+
+
+1 => array(     'Name' => "Nikon Makernote Version",
+                'Type' => "Special" ),
+
+2 => array(     'Name' => "ISO Speed Used",
+                'Type' => "Special" ),
+
+3 => array(     'Name' => "Colour Mode",
+                'Type' => "String" ),
+
+4 => array(     'Name' => "Quality",
+                'Type' => "String" ),
+
+5 => array(     'Name' => "White Balance",
+                'Type' => "String" ),
+
+6 => array(     'Name' => "Sharpening",
+                'Type' => "String" ),
+
+7 => array(     'Name' => "Focus Mode",
+                'Type' => "String" ),
+
+8 => array(     'Name' => "Flash Setting",
+                'Type' => "String" ),
+
+9 => array(     'Name' => "Auto Flash Mode",
+                'Type' => "String"  ),
+
+11 => array(    'Name' => "White Balance Bias Value",
+                'Type' => "Numeric",
+                'Units' => "(Units Approx: 100 Mired per increment)" ),
+
+12 => array(    'Name' => "White Balance Red, Blue Coefficients?",
+                'Type' => "Numeric"  ),
+
+15 => array(    'Name' => "ISO Selection?",
+                'Type' => "String" ),
+
+
+18 => array(    'Name' => "Flash Compensation",
+                'Type' => "Lookup",
+                0x06 => "+1.0 EV",
+                0x04 => "+0.7 EV",
+                0x03 => "+0.5 EV",
+                0x02 => "+0.3 EV",
+                0x00 => "0.0 EV",
+                0xfe => "-0.3 EV",
+                0xfd => "-0.5 EV",
+                0xfc => "-0.7 EV",
+                0xfa => "-1.0 EV",
+                0xf8 => "-1.3 EV",
+                0xf7 => "-1.5 EV",
+                0xf6 => "-1.7 EV",
+                0xf4 => "-2.0 EV",
+                0xf2 => "-2.3 EV",
+                0xf1 => "-2.5 EV",
+                0xf0 => "-2.7 EV",
+                0xee => "-3.0 EV" ),
+
+19 => array(    'Name' => "ISO Speed Requested",
+                'Type' => "Special",
+                'Units' => "(May be different to Speed Used when Auto ISO is on)" ),
+
+
+22 => array(    'Name' => "Photo corner coordinates",
+                'Type' => "Numeric",
+                'Units' => "Pixels"  ),
+
+24 => array(    'Name' => "Flash Bracket Compensation Applied",
+                'Type' => "Lookup",
+                0x06 => "+1.0 EV",
+                0x04 => "+0.7 EV",
+                0x03 => "+0.5 EV",
+                0x02 => "+0.3 EV",
+                0x00 => "0.0 EV",
+                0xfe => "-0.3 EV",
+                0xfd => "-0.5 EV",
+                0xfc => "-0.7 EV",
+                0xfa => "-1.0 EV",
+                0xf8 => "-1.3 EV",
+                0xf7 => "-1.5 EV",
+                0xf6 => "-1.7 EV",
+                0xf4 => "-2.0 EV",
+                0xf2 => "-2.3 EV",
+                0xf1 => "-2.5 EV",
+                0xf0 => "-2.7 EV",
+                0xee => "-3.0 EV" ),
+
+25 => array(    'Name' => "AE Bracket Compensation Applied",
+                'Type' => "Numeric",
+                'Units' => "EV" ),
+
+128 => array(   'Name' => "Image Adjustment?",
+                'Type' => "String" ),
+
+129 => array(   'Name' => "Tone Compensation (Contrast)",
+                'Type' => "String" ),
+
+130 => array(   'Name' => "Auxiliary Lens (Adapter)",
+                'Type' => "String" ),
+
+131 => array(   'Name' => "Lens Type?",
+                'Type' => "Lookup",
+                6 => "Nikon D series Lens",
+                14 => "Nikon G series Lens" ),
+
+132 => array(   'Name' => "Lens Min/Max Focal Length, Min/Max Aperture",
+                'Type' => "Numeric",
+                'Units' => " mm, mm, F#, F#" ),
+
+133 => array(   'Name' => "Manual Focus Distance?",
+                'Type' => "Numeric"),
+
+134 => array(   'Name' => "Digital Zoom Factor?",
+                'Type' => "Numeric" ),
+
+135 => array(   'Name' => "Flash Used",
+                'Type' => "Lookup",
+                0 => "Flash Not Used",
+                9 => "Flash Fired" ),
+
+136 => array(   'Name' => "Auto Focus Area",
+                'Description' => "byte 1 : AF Mode: 00 = single area, 01 = Dynamic Area, 02 = Closest Subject\n
+                                  byte 2 : AF Area Selected : 00 = Centre, 01 = Top, 02 = Bottom, 03 = Left, 04 = Right\n
+                                  byte 3 : Unknown, always zero\n
+                                  byte 4 : Properly focused Area(s) : bit 0 = Centre, bit 1 = Top, bit 2 = Bottom, bit 3 = Left, bit 4 = Right",
+                'Type' => "Special" ),
+
+137 => array(   'Name' => "Bracketing & Shooting Mode",
+                'Description' => "bit 0&1 (0 = single frame, 1 = continuous,2=timer, 3=remote timer? 4 = remote?\n
+                                  bit 4, Bracketing on or off\n
+                                  bit 6, white Balance Bracketing on",
+                'Type' => "Special" ),
+
+141 => array(   'Name' => "Colour Mode",
+                'Description' =>"1a = Portrait sRGB, 2 = Adobe RGB, 3a = Landscape sRGB",
+                'Type' => "String" ),
+
+143 => array(   'Name' => "Scene Mode?",
+                'Type' => "Numeric" ),
+
+144 => array(   'Name' => "Lighting Type",
+                'Type' => "String" ),
+
+146 => array(   'Name' => "Hue Adjustment",
+                'Type' => "Numeric",
+                'Units' => "Degrees" ),
+
+148 => array(   'Name' => "Saturation?",
+                'Type' => "Lookup",
+                -3 => "Black and White",
+                -2 => "-2",
+                -1 => "-1",
+                0 =>  "Normal",
+                1 =>  "+1",
+                2 =>  "+2" ),
+
+149 => array(   'Name' => "Noise Reduction",
+                'Type' => "String" ),
+
+167 => array(   'Name' => "Total Number of Shutter Releases for Camera",
+                'Type' => "Numeric",
+                'Units' => "Shutter Releases" ),
+
+169 => array(   'Name' => "Image optimisation",
+                'Type' => "String" ),
+
+170 => array(   'Name' => "Saturation",
+                'Type' => "String" ),
+
+171 => array(   'Name' => "Digital Vari-Program",
+                'Type' => "String" )
+
+
+// Tags that exist but are unknown: 10, 13, 14, 16, 17, 23, 24, 138, 139, 145,
+// 151, 152, 160, 162 163, 165, 166, 168
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Nikon Type 3
+******************************************************************************/
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/RCS/canon.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/canon.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/canon.php	2011/08/17 20:12:03	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Makernotes/canon.php	2004/08/05 14:09:16
@@ -0,0 +1,748 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     canon.php
+*
+* Description:  Canon Makernote Parser
+*               Provides functions to decode an Canon EXIF makernote and to interpret
+*               the resulting array into html.
+*
+*               Canon Makernote Format:
+*
+*               Field           Size            Description
+*               ----------------------------------------------------------------
+*               IFD Data        Variable        Standard IFD Data using Canon Tags
+*               ----------------------------------------------------------------
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*               This file may be used freely for non-commercial purposes.For
+*               commercial uses please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+// Add the parser and interpreter functions to the list of Makernote parsers and interpreters.
+
+$GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'][] = "get_Canon_Makernote";
+$GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'][] = "get_Canon_Text_Value";
+$GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'][] = "get_Canon_Makernote_Html";
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Canon_Makernote
+*
+* Description:  Decodes the Makernote tag and returns the new tag with the decoded
+*               information attached. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*               Make_Field - The contents of the EXIF Make field, to aid
+*                            determining whether this script can decode
+*                            the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Canon_Makernote( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field )
+{
+        // Check if the Make Field contains the word Canon
+        if ( stristr( $Make_Field, "Canon" ) === FALSE )
+        {
+                // Canon not found in Make Field - can't process this
+                return FALSE;
+        }
+        
+        // Seek to the start of the IFD
+
+        fseek($filehnd, $Makernote_Tag['Tiff Offset'] + $Makernote_Tag['Offset']  );
+
+        // Read the IFD(s) into an array
+        $Makernote_Tag['Decoded Data'] = read_Multiple_IFDs( $filehnd, $Makernote_Tag['Tiff Offset'], $Makernote_Tag['ByteAlign'], "Canon" );
+
+        // Save some information into the Tag element to aid interpretation
+        $Makernote_Tag['Decoded'] = TRUE;
+        $Makernote_Tag['Makernote Type'] = "Canon";
+        $Makernote_Tag['Makernote Tags'] = "Canon";
+
+        
+        // Return the new tag
+        return $Makernote_Tag;
+}
+
+/******************************************************************************
+* End of Function:     get_Canon_Makernote
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Canon_Makernote_Html
+*
+* Description:  Attempts to interpret a makernote into html. Returns false if
+*               it is not a makernote that can be processed with this script
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*               FALSE - If this script could not interpret the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Canon_Makernote_Html( $Makernote_tag, $filename )
+{
+        // Check that this makernote uses canon tags
+        if ( $Makernote_tag['Makernote Type'] != "Canon" )
+        {
+                // Makernote doesn't use Canon tags - cant Interpret it
+                return FALSE;
+        }
+
+        // Interpret the IFD to html
+        return interpret_IFD( $Makernote_tag['Decoded Data'][0], $filename );
+
+}
+
+/******************************************************************************
+* End of Function:     get_Canon_Makernote_Html
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Canon_Text_Value
+*
+* Description:  Provides a text value for any tag marked as special for makernotes
+*               that this script can decode. Returns false if this is not a makernote
+*               that can be processed with this script
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from get_Canon_Makernote
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If this script could not decode the makernote, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Canon_Text_Value( $Exif_Tag, $Tag_Definitions_Name )
+{
+
+        // Check that the tag uses Canon Definitions
+        if ( $Tag_Definitions_Name != "Canon" )
+        {
+                // Tag doesn't use Canon definintions - can't process it
+                return FALSE;
+        }
+
+
+        $Tag_ID = $Exif_Tag['Tag Number'];
+
+
+        // Process the special tag according to the tag number
+        switch ( $Tag_ID )
+        {
+
+                // CAMERA SETTINGS 1
+                case 1:
+                        // Create an output string
+                        $output_str = "";
+
+                        // Cycle through each of the camera settings Values
+                        foreach( $Exif_Tag['Data'] as $offset => $value )
+                        {
+                                // Check that the value exists
+                                if ( $value !== NULL )
+                                {
+                                        // Process the settings according to their offset
+                                        if ( $offset == 0 )
+                                        {
+                                                // Do Not Show this Field ( Number of Bytes in Tag )
+                                        }
+                                        else if ( $offset == 2 )
+                                        {
+                                                if ( $value == 0 )
+                                                {
+                                                        $output_str .= "Self timer not used\n";
+                                                }
+                                                else
+                                                {
+                                                        $output_str .= "Self timer length : ". ($value/10) . " seconds\n";
+                                                }
+                                        }
+                                        else if ( ( $offset == 23 ) && ( $Exif_Tag['Data'][25] != 0 ))
+                                        {
+                                                $output_str .= "Maximum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n";
+                                        }
+                                        else if ( ( $offset == 24 ) && ( $Exif_Tag['Data'][25] != 0 ))
+                                        {
+                                                $output_str .= "Minimum Focal Length of Lens: " . ($value / $Exif_Tag['Data'][25]) . "mm\n";
+                                        }
+                                        else if ( $offset == 25 )
+                                        {
+                                                // Do Not Show this Field ( Focal Length units per mm )
+                                        }
+                                        else if ( $offset == 29 )
+                                        {
+                                                if ( $value & 0x4000 == 0x4000 )
+                                                {
+                                                        $output_str .= "External E-TTL Flash\n";
+                                                }
+                                                if ( $value & 0x2000 == 0x2000 )
+                                                {
+                                                        $output_str .= "Internal Flash\n";
+                                                }
+                                                if ( $value & 0x0800 == 0x0800 )
+                                                {
+                                                        $output_str .= "Flash FP sync used\n";
+                                                }
+                                                if ( $value & 0x0080 == 0x0080 )
+                                                {
+                                                        $output_str .= "Second (Rear) curtain flash sync used\n";
+                                                }
+                                                if ( $value & 0x0008 == 0x0008 )
+                                                {
+                                                        $output_str .= "Flash FP sync enabled\n";
+                                                }
+
+                                        }
+                                        else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] ) )
+                                        {
+                                                if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset] ) )
+                                                {
+                                                        $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset][$value] . "\n";
+                                                }
+                                                else
+                                                {
+                                                        if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                        {
+                                                                $output_str .= $GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n";
+                                                        }
+                                                }
+                                        }
+                                        else
+                                        {
+                                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                {
+                                                        // Unknown Canon camera setting
+                                                        $output_str .= "        Unknown Setting ($offset), value: $value\n";
+                                                }
+                                        }
+                                }
+
+                        }
+                        // Return the text string
+                        return $output_str;
+                        break;
+
+
+                // CAMERA SETTINGS 2
+                case 4:
+                        // Create an output string
+                        $output_str = "";
+
+                        // Cycle through each of the camera settings Values
+                        foreach( $Exif_Tag['Data'] as $offset => $value )
+                        {
+                                // Check that the value exists
+                                if ( $value !== NULL )
+                                {
+                                        // Process the settings according to their offset
+                                        if ( $offset == 0 )
+                                        {
+                                                // Do Not Show this Field ( Number of Bytes in Tag )
+                                        }
+                                        else if ( $offset == 9 )
+                                        {
+                                                $output_str .= "Sequence Number in a continuous burst : $value\n";
+                                        }
+                                        else if ( $offset == 14 )
+                                        {
+                                                $output_str .= "Number of Focus Points Available: ". ( ( $value & 0xF000 ) / 0x1000 ) . "\n";
+
+                                                if ( $value & 0x0004 == 0x0004 )
+                                                {
+                                                        $output_str .= "Left Focus Point Used\n";
+                                                }
+                                                if ( $value & 0x0002 == 0x0002 )
+                                                {
+                                                        $output_str .= "Centre Focus Point Used\n";
+                                                }
+                                                if ( $value & 0x0001 == 0x0001 )
+                                                {
+                                                        $output_str .= "Right Focus Point Used\n";
+                                                }
+                                        }
+                                        else if ( $offset == 19 )
+                                        {
+                                                $output_str .= "Subject distance: $value (units either mm or cm)\n";
+                                        }
+                                        else if ( array_key_exists( $offset, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] ) )
+                                        {
+                                                if ( array_key_exists( $value, $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset] ) )
+                                                {
+                                                        $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": " . $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset][$value] . "\n";
+                                                }
+                                                else
+                                                {
+                                                        if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                        {
+                                                                $output_str .= $GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ][$offset]['Name'] . ": Unknown Value ($value)\n";
+                                                        }
+                                                }
+                                        }
+                                        else
+                                        {
+                                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                {
+                                                        $output_str .= "        Unknown Setting ($offset), value: $value\n";
+                                                }
+                                        }
+                                }
+
+                        }
+                        // Return the text string
+                        return $output_str;
+                        break;
+                        
+                        
+                // Serial Number
+                case 12:
+                        $output_str =  sprintf ( "%04X%05d", (($Exif_Tag['Data'][0] & 0xFF00)/256), ($Exif_Tag['Data'][0] & 0x00FF) );
+                        break;
+
+
+                // Custom Functions
+                case 15:
+                        // Create an output string
+                        $output_str = "";
+
+                        // The size element is the first of the value array
+                        // get rid of it
+                        $tmparray = $Exif_Tag['Data'];
+                        array_shift ( $tmparray );
+
+                        // Cycle through each of the custom functions
+                        foreach( $tmparray as $valorder => $value )
+                        {
+                                // Figure out the function number and value
+                                $funcno = ( $value & 0xFF00 ) / 256;
+                                $funcval = $value & 0x00FF;
+
+                                // Check if the function exists in the lookup table of custom functions
+                                if ( array_key_exists( $funcno, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] ) )
+                                {
+                                        // Function Exists in lookup table,
+                                        // Check if value exists for this function in the lookup table
+                                        if ( array_key_exists( $funcval, $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno] ) )
+                                        {
+                                                // Value exists - Add it to the output text
+                                                $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": " . $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno][$funcval] . "\n";
+                                        }
+                                        else
+                                        {
+                                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                                {
+                                                        // Value doesn't exist - Add a message to the output text
+                                                        $output_str .= $GLOBALS[ "Canon_Custom_Functions_Tag_Values" ][$funcno]['Name'] . ": Unknown Value ($value)\n";
+                                                }
+                                        }
+                                }
+                                else
+                                {
+                                        if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                                        {
+                                                // Function doesn't exist in lookup table - add a message to the output text
+                                                $output_str .= "Unknown Custom Function ($funcno), value: $funcval\n";
+                                        }
+                                }
+                        }
+                        // Return the resulting string
+                        return $output_str;
+                        break;
+
+                default :
+                        return FALSE;
+        }
+        
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_Canon_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions, Canon
+*
+* Contents:     This global variable provides definitions of the known Canon
+*               Makernote tags, indexed by their tag number.
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ]['Canon'] = array(
+
+1 => array(     'Name' => "Camera Settings 1",
+                'Type' => "Special" ),
+
+4 => array(     'Name' => "Camera Settings 2",
+                'Type' => "Special" ),
+
+6 => array(     'Name' => "Image Type",
+                'Type' => "String" ),
+
+7 => array(     'Name' => "Firmware Version",
+                'Type' => "String" ),
+
+8 => array(     'Name' => "Image Number",
+                'Type' => "Numeric" ),
+
+9 => array(     'Name' => "Owner Name",
+                'Type' => "String" ),
+
+12 => array(    'Name' => "Camera Serial Number",
+                'Type' => "Special" ),
+
+15 => array(    'Name' => "Custom Functions",
+                'Type' => "Special" )
+
+);
+
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions, Canon
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Canon_Camera_Settings_1_Tag_Values
+*
+* Contents:     This global variable provides definitions for the Canon Camera
+*               Settings 1 Makernote tag, indexed by their offset.
+*
+******************************************************************************/
+
+$GLOBALS[ "Canon_Camera_Settings_1_Tag_Values" ] = array(
+
+1 => array(     'Name' => "Macro Mode",
+                1 => "Macro",
+                2 => "Normal ( Not Macro )" ),
+
+3 => array(     'Name' => "Quality",
+                2 => "Normal",
+                3 => "Fine",
+                5 => "Superfine" ),
+
+4 => array(     'Name' => "Flash Mode",
+                0 => "Flash Not Fired",
+                1 => "Auto",
+                2 => "On",
+                3 => "Red Eye Reduction",
+                4 => "Slow Synchro",
+                5 => "Auto + Red Eye Reduction",
+                6 => "On + Red Eye Reduction",
+                16 => "External Flash" ),
+
+5 => array(     'Name' => "Continuous drive mode",
+                0 => "Single Frame or Timer Mode",
+                1 => "Continuous" ),
+
+7 => array(     'Name' => "Focus Mode",
+                0 => "One-Shot",
+                1 => "AI Servo",
+                2 => "AI Focus",
+                3 => "Manual Focus",
+                4 => "Single",
+                5 => "Continuous",
+                6 => "Manual Focus" ),
+
+10 => array(    'Name' => "Image Size",
+                0 => "Large",
+                1 => "Medium",
+                2 => "Small" ),
+
+11 => array(    'Name' => "Easy shooting Mode",
+                0 => "Full Auto",
+                1 => "Manual",
+                2 => "Landscape",
+                3 => "Fast Shutter",
+                4 => "Slow Shutter",
+                5 => "Night",
+                6 => "Black & White",
+                7 => "Sepia",
+                8 => "Portrait",
+                9 => "Sports",
+                10 => "Macro / Close-Up",
+                11 => "Pan Focus" ),
+
+
+12 => array(    'Name' => "Digital Zoom",
+                0 => "No Digital Zoom",
+                1 => "2x",
+                2 => "4x" ),
+
+13 => array(    'Name' => "Contrast",
+                0 => "Normal",
+                1 => "High",
+                65535 => "Low" ),
+
+14 => array(    'Name' => "Saturation",
+                0 => "Normal",
+                1 => "High",
+                65535 => "Low" ),
+
+15 => array(    'Name' => "Sharpness",
+                0 => "Normal",
+                1 => "High",
+                65535 => "Low" ),
+
+16 => array(    'Name' => "ISO Speed",
+                0 => "Check ISOSpeedRatings EXIF tag for ISO Speed",
+                15 => "Auto ISO",
+                16 => "ISO 50",
+                17 => "ISO 100",
+                18 => "ISO 200",
+                19 => "ISO 400" ),
+
+17 => array(    'Name' => "Metering Mode",
+                3 => "Evaluative",
+                4 => "Partial",
+                5 => "Centre Weighted" ),
+
+18 => array(    'Name' => "Focus Type",
+                0 => "Manual",
+                1 => "Auto",
+                3 => "Close-up (Macro)",
+                8 => "Locked (Pan Mode)" ),
+
+19 => array(    'Name' => "Auto Focus Point Selected",
+                12288 => "None (Manual Focus)",
+                12289 => "Auto Selected",
+                12290 => "Right",
+                12291 => "Centre",
+                12292 => "Left" ),
+
+20 => array(    'Name' => "Exposure Mode",
+                0 => "Easy Shooting (See Easy Shooting Mode)",
+                1 => "Program",
+                2 => "Tv-Priority",
+                3 => "Av-Priority",
+                4 => "Manual",
+                5 => "A-DEP" ),
+
+28 => array(    'Name' => "Flash Activity",
+                0 => "Flash Did Not Fire",
+                1 => "Flash Fired" ),
+
+32 => array(    'Name' => "Focus Mode",
+                0 => "Focus Mode: Single",
+                1 => "Focus Mode: Continuous" )
+
+);
+
+/******************************************************************************
+* End of Global Variable:     Canon_Camera_Settings_1_Tag_Values
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+* Global Variable:      Canon_Camera_Settings_2_Tag_Values
+*
+* Contents:     This global variable provides definitions for the Canon Camera
+*               Settings 2 Makernote tag, indexed by their offset.
+*
+******************************************************************************/
+
+$GLOBALS[ "Canon_Camera_Settings_2_Tag_Values" ] = array(
+
+7 => array (    'Name' => "White Balance",
+                0 => "Auto",
+                1 => "Sunny",
+                2 => "Cloudy",
+                3 => "Tungsten",
+                4 => "Flourescent",
+                5 => "Flash",
+                6 => "Custom" ),
+
+15 => array(    'Name' => "Flash Bias",
+                0xffc0 => "-2 EV",
+                0xffcc => "-1.67 EV",
+                0xffd0 => "-1.5 EV",
+                0xffd4 => "-1.33 EV",
+                0xffe0 => "-1 EV",
+                0xffec => "-0.67 EV",
+                0xfff0 => "-0.5 EV",
+                0xfff4 => "-0.33 EV",
+                0x0000 => "0 EV",
+                0x000c => "0.33 EV",
+                0x0010 => "0.5 EV",
+                0x0014 => "0.67 EV",
+                0x0020 => "1 EV",
+                0x002c => "1.33 EV",
+                0x0030 => "1.5 EV",
+                0x0034 => "1.67 EV",
+                0x0040 => "2 EV" ),
+);
+
+/******************************************************************************
+* End of Global Variable:     Canon_Camera_Settings_2_Tag_Values
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Canon_Custom_Functions_Tag_Values
+*
+* Contents:     This global variable provides definitions for the Canon Custom
+*               Functions Makernote tag, indexed by their offset.
+*
+******************************************************************************/
+
+$GLOBALS[ "Canon_Custom_Functions_Tag_Values" ] = array(
+
+1 => array (    'Name' => "Long Exposure Noise Reduction",
+                0 => "Off",
+                1 => "On" ),
+
+2 => array (    'Name' => "Shutter/Auto Exposure-lock buttons",
+                0 => "AF/AE lock",
+                1 => "AE lock/AF",
+                2 => "AF/AF lock",
+                3 => "AE+release/AE+AF" ),
+
+3 => array (    'Name' => "Mirror lockup",
+                0 => "Disable",
+                1 => "Enable" ),
+
+4 => array (    'Name' => "Tv/Av and exposure level",
+                0 => "1/2 stop",
+                1 => "1/3 stop" ),
+
+5 => array (    'Name' => "AF-assist light",
+                0 => "On (Auto)",
+                1 => "Off" ),
+
+6 => array (    'Name' => "Shutter speed in Av mode",
+                0 => "Automatic",
+                1 => "1/200 (fixed)" ),
+
+7 => array (    'Name' => "Auto-Exposure Bracketting sequence/auto cancellation",
+                0 => "0,-,+ / Enabled",
+                1 => "0,-,+ / Disabled",
+                2 => "-,0,+ / Enabled",
+                3 => "-,0,+ / Disabled" ),
+
+8 => array (    'Name' => "Shutter Curtain Sync",
+                0 => "1st Curtain Sync",
+                1 => "2nd Curtain Sync" ),
+
+9 => array (    'Name' => "Lens Auto-Focus stop button Function Switch",
+                0 => "AF stop",
+                1 => "Operate AF",
+                2 => "Lock AE and start timer" ),
+
+10 => array (   'Name' => "Auto reduction of fill flash",
+                0 => "Enable",
+                1 => "Disable" ),
+
+11 => array (   'Name' => "Menu button return position",
+                0 => "Top",
+                1 => "Previous (volatile)",
+                2 => "Previous" ),
+
+12 => array (   'Name' => "SET button function when shooting",
+                0 => "Not Assigned",
+                1 => "Change Quality",
+                2 => "Change ISO Speed",
+                3 => "Select Parameters" ),
+
+13 => array (   'Name' => "Sensor cleaning",
+                0 => "Disable",
+                1 => "Enable" )
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     Canon_Custom_Functions_Tag_Values
+******************************************************************************/
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Edit_File_Info.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info.php	2011/08/17 20:08:58	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info.php	2005/01/20 10:37:28
@@ -0,0 +1,575 @@
+<?php
+
+
+/****************************************************************************
+*
+* Filename:     Edit_File_Info.php
+*
+* Description:  Allows the user to edit the metadata of an image over the internet
+*               in the same way that Photoshop edits 'File Info' data
+*               This file provides only the html for a form containing the file info
+*               input fields. The rest of the html file must be provided by the calling script.
+*               $outputfilename must always be defined - it is ne name of the file which
+*               have the metadata changed after the form has been submitted
+*
+*               This file has several modes of operation:
+*
+*               1) If $new_ps_file_info_array is defined then it's data will be used
+*                  to fill the fields.
+*               2) If $new_ps_file_info_array is not defined but $filename is defined,
+*                  then the file info fields will be filled from the metadata in the file specified
+*               3) If $new_ps_file_info_array is not defined but $filename and $default_ps_file_info_array
+*                  are defined, then the file info fields will be filled from the metadata
+*                  in the file specified, but where fields are blank, they will be filled from $default_ps_file_info_array
+*               4) Otherwise the fields will be blank
+*
+*               See Edit_File_Info_Example.php for an example of usage
+*
+* Author:       Evan Hunter
+*
+* Date:         17/11/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+***************************************************************************/
+
+
+
+        include 'Toolkit_Version.php';          // Change: added as of version 1.11
+
+        // Check for operation modes 2 or 3
+        // i.e. $filename is defined, and $new_ps_file_info_array is not
+        if ( ( ! isset( $new_ps_file_info_array ) ) &&
+             ( isset( $filename ) ) &&
+             ( is_string( $filename ) ) )
+        {
+                // Hide any unknown EXIF tags
+                $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE;
+
+                // Accessing the existing file info for the specified file requires these includes
+                include 'JPEG.php';
+                include 'XMP.php';
+                include 'Photoshop_IRB.php';
+                include 'EXIF.php';
+                include 'Photoshop_File_Info.php';
+
+                // Retrieve the header information from the JPEG file
+                $jpeg_header_data = get_jpeg_header_data( $filename );
+
+                // Retrieve EXIF information from the JPEG file
+                $Exif_array = get_EXIF_JPEG( $filename );
+
+                // Retrieve XMP information from the JPEG file
+                $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
+
+                // Retrieve Photoshop IRB information from the JPEG file
+                $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
+
+                // Retrieve Photoshop File Info from the three previous arrays
+                $new_ps_file_info_array = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array );
+
+
+
+                // Check if there is an array of defaults available
+                if ( ( isset( $default_ps_file_info_array) ) &&
+                     ( is_array( $default_ps_file_info_array) ) )
+                {
+                        // There are defaults defined
+
+                        // Check if there is a default for the date defined
+                        if ( ( ! array_key_exists( 'date', $default_ps_file_info_array ) ) ||
+                             ( ( array_key_exists( 'date', $default_ps_file_info_array ) ) &&
+                               ( $default_ps_file_info_array['date'] == '' ) ) )
+                        {
+                                // No default for the date defined
+                                // figure out a default from the file
+
+                                // Check if there is a EXIF Tag 36867 "Date and Time of Original"
+                                if ( ( $Exif_array != FALSE ) &&
+                                     ( array_key_exists( 0, $Exif_array ) ) &&
+                                     ( array_key_exists( 34665, $Exif_array[0] ) ) &&
+                                     ( array_key_exists( 0, $Exif_array[0][34665] ) ) &&
+                                     ( array_key_exists( 36867, $Exif_array[0][34665][0] ) ) )
+                                {
+                                        // Tag "Date and Time of Original" found - use it for the default date
+                                        $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36867]['Data'][0];
+                                        $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] );
+                                }
+                               // Check if there is a EXIF Tag 36868 "Date and Time when Digitized"
+                                else if ( ( $Exif_array != FALSE ) &&
+                                     ( array_key_exists( 0, $Exif_array ) ) &&
+                                     ( array_key_exists( 34665, $Exif_array[0] ) ) &&
+                                     ( array_key_exists( 0, $Exif_array[0][34665] ) ) &&
+                                     ( array_key_exists( 36868, $Exif_array[0][34665][0] ) ) )
+                                {
+                                        // Tag "Date and Time when Digitized" found - use it for the default date
+                                        $default_ps_file_info_array['date'] = $Exif_array[0][34665][0][36868]['Data'][0];
+                                        $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] );
+                                }
+                                // Check if there is a EXIF Tag 306 "Date and Time"
+                                else if ( ( $Exif_array != FALSE ) &&
+                                     ( array_key_exists( 0, $Exif_array ) ) &&
+                                     ( array_key_exists( 306, $Exif_array[0] ) ) )
+                                {
+                                        // Tag "Date and Time" found - use it for the default date
+                                        $default_ps_file_info_array['date'] = $Exif_array[0][306]['Data'][0];
+                                        $default_ps_file_info_array['date'] = preg_replace( "/(\d\d\d\d):(\d\d):(\d\d)( \d\d:\d\d:\d\d)/", "$1-$2-$3", $default_ps_file_info_array['date'] );
+                                }
+                                else
+                                {
+                                        // Couldn't find an EXIF date in the image
+                                        // Set default date as creation date of file
+                                        $default_ps_file_info_array['date'] = date ("Y-m-d", filectime( $filename ));
+                                }
+                        }
+
+                        // Cycle through all the elements of the default values array
+                        foreach( $default_ps_file_info_array as $def_key =>$default_item )
+                        {
+                                // Check if the current element is Keywords or
+                                // Supplemental Categories as these are arrays
+                                // and need to be treated differently
+                                if ( ( strcasecmp( $def_key, "keywords" ) == 0 ) ||
+                                     ( strcasecmp( $def_key, "supplementalcategories" ) == 0 ) )
+                                {
+                                        // Keywords or Supplemental Categories found
+                                        // Check if the File Info from the file is empty for this element
+                                        // and if there are default values in this array element
+                                        if ( ( count( $new_ps_file_info_array[ $def_key ] ) == 0 ) &&
+                                             ( is_array( $default_item ) ) &&
+                                             ( count( $default_item ) >= 0 ) )
+                                        {
+                                                // The existing file info is empty, and there are
+                                                // defaults - add them
+                                                $new_ps_file_info_array[ $def_key ] = $default_item;
+                                        }
+                                }
+                                // Otherwise, this is not an array element, just check if it is blank in the existing file info
+                                else if ( trim( $new_ps_file_info_array[ $def_key ] ) == "" )
+                                {
+                                        // The existing file info is blank, add the default value
+                                        $new_ps_file_info_array[ $def_key ] = $default_item;
+                                }
+
+                        }
+                }
+        }
+        // Check for operation mode 4 - $new_ps_file_info_array and $filename are not defined,
+        else if ( ( ( !isset($new_ps_file_info_array) ) || ( ! is_array($new_ps_file_info_array) ) ) &&
+                  ( ( !isset($filename) ) || ( ! is_string( $filename ) ) ) )
+        {
+                // No filename or new_ps_file_info_array defined, create a blank file info array to display
+                $new_ps_file_info_array = array(
+                      "title" => "",
+                      "author" => "",
+                      "authorsposition" => "",
+                      "caption" => "",
+                      "captionwriter" => "",
+                      "jobname" => "",
+                      "copyrightstatus" => "",
+                      "copyrightnotice" => "",
+                      "ownerurl" => "",
+                      "keywords" => array(),
+                      "category" => "",
+                      "supplementalcategories" => array(),
+                      "date" => "",
+                      "city" => "",
+                      "state" => "",
+                      "country" => "",
+                      "credit" => "",
+                      "source" => "",
+                      "headline" => "",
+                      "instructions" => "",
+                      "transmissionreference" => "",
+                      "urgency" => "" );
+        }
+
+
+
+/***************************************************************************
+*
+* Now output the actual HTML form
+*
+***************************************************************************/
+
+?>
+
+
+
+
+        <form name="EditJPEG" action="Write_File_Info.php" method="post">
+
+
+        <?php echo "<input name=\"filename\" type=\"hidden\" value=\"$outputfilename\">"; ?>
+
+                <table>
+
+                        <tr>
+                                <td>
+                                        Title
+                                </td>
+                                <td>
+                                        <?php
+                                        echo "<input size=49 name=\"title\" type=\"text\" value=\"". $new_ps_file_info_array[ 'title' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Author
+                                </td>
+                                <td>
+                                        <?php
+                                        echo "<input size=49 name=\"author\" type=\"text\" value=\"". $new_ps_file_info_array[ 'author' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Authors Position
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"authorsposition\" type=\"text\" value=\"". $new_ps_file_info_array[ 'authorsposition' ] ."\"> - Note: not used in Photoshop 7 or higher";
+                                        ?>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Description
+                                </td>
+                                <td>
+                                        <textarea name="caption" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'caption' ]; ?></textarea>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Description Writer
+                                </td>
+                                <td>
+                                        <?php
+                                        echo "<input size=49 name=\"captionwriter\" type=\"text\" value=\"". $new_ps_file_info_array[ 'captionwriter' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Keywords
+                                </td>
+                                <td>
+                                        <textarea name="keywords" rows=3 cols=37 wrap="off"><?php
+                                                                                                foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword )
+                                                                                                {
+                                                                                                        echo "$keyword&#xA;";
+                                                                                                }
+                                                                                            ?></textarea>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Copyright Status
+                                </td>
+                                <td>
+                                        <select size=1  name="copyrightstatus">
+                                                <?php
+                                                        $copystatus = $new_ps_file_info_array[ 'copyrightstatus' ];
+                                                        if ( $copystatus == "Unknown" )
+                                                        {
+                                                                echo "<option value=\"Unknown\" SELECTED >Unknown</option>\n";
+                                                        }
+                                                        else
+                                                        {
+                                                                echo "<option value=\"Unknown\">Unknown</option>\n";
+                                                        }
+
+                                                        if ( $copystatus == "Copyrighted Work" )
+                                                        {
+                                                                echo "<option value=\"Copyrighted Work\" SELECTED >Copyrighted Work</option>\n";
+                                                        }
+                                                        else
+                                                        {
+                                                                echo "<option value=\"Copyrighted Work\">Copyrighted Work</option>\n";
+                                                        }
+
+                                                        if ( $copystatus == "Public Domain" )
+                                                        {
+                                                                echo "<option value=\"Public Domain\" SELECTED >Public Domain</option>\n";
+                                                        }
+                                                        else
+                                                        {
+                                                                echo "<option value=\"Public Domain\">Public Domain</option>\n";
+                                                        }
+                                                ?>
+                                        </select>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Copyright Notice
+                                </td>
+                                <td>
+                                        <textarea name="copyrightnotice" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'copyrightnotice' ]; ?></textarea>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Copyright Info URL
+                                </td>
+                                <td>
+                                        <?php
+                                        echo "<input size=49 name=\"ownerurl\" type=\"text\" value=\"". $new_ps_file_info_array[ 'ownerurl' ] ."\">\n";
+                                        if ($new_ps_file_info_array[ 'ownerurl' ] != "" )
+                                        {
+                                                echo "<a href=\"". $new_ps_file_info_array[ 'ownerurl' ] ."\" > (". $new_ps_file_info_array[ 'ownerurl' ] .")</a>\n";
+                                        }
+                                        ?>
+
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Category
+                                </td>
+                                <td>
+                                        <?php
+                                        echo "<input size=49 name=\"category\" type=\"text\" value=\"". $new_ps_file_info_array[ 'category' ] ."\">\n";
+                                        ?>
+
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Supplemental Categories
+                                </td>
+                                <td>
+                                        <textarea name="supplementalcategories" rows=3 cols=37 wrap="off"><?php
+                                                                                                foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat )
+                                                                                                {
+                                                                                                        echo "$supcat&#xA;";
+                                                                                                }
+                                                                                            ?>
+                                        </textarea>
+                                </td>
+                        </tr>
+
+
+
+                        <tr>
+                                <td>
+                                        Date Created
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"date\" type=\"text\" value=\"". $new_ps_file_info_array[ 'date' ] ."\">";
+                                        ?>
+                                        (Note date must be YYYY-MM-DD format)
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        City
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"city\" type=\"text\" value=\"". $new_ps_file_info_array[ 'city' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        State
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"state\" type=\"text\" value=\"". $new_ps_file_info_array[ 'state' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Country
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"country\" type=\"text\" value=\"". $new_ps_file_info_array[ 'country' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+
+                        <tr>
+                                <td>
+                                        Credit
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"credit\" type=\"text\" value=\"". $new_ps_file_info_array[ 'credit' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Source
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"source\" type=\"text\" value=\"". $new_ps_file_info_array[ 'source' ] ."\">";
+                                        ?>
+                                </td>
+                        </tr>
+
+
+
+                        <tr>
+                                <td>
+                                        Headline
+                                </td>
+                                <td>
+                                        <textarea name="headline" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'headline' ]; ?></textarea>
+                                </td>
+                        </tr>
+
+
+
+                        <tr>
+                                <td>
+                                        Instructions
+                                </td>
+                                <td>
+                                        <textarea name="instructions" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'instructions' ]; ?></textarea>
+                                </td>
+                        </tr>
+
+
+                        <tr>
+                                <td>
+                                        Transmission Reference
+                                </td>
+                                <td>
+                                        <textarea name="transmissionreference" rows=3 cols=37 wrap="off"><?php echo $new_ps_file_info_array[ 'transmissionreference' ]; ?></textarea>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Job Name
+                                </td>
+                                <td>
+                                        <?php
+                                                echo "<input size=49 name=\"jobname\" type=\"text\" value=\"". $new_ps_file_info_array[ 'jobname' ] ."\"> - Note: not used in Photoshop CS";
+                                        ?>
+                                </td>
+                        </tr>
+
+                        <tr>
+                                <td>
+                                        Urgency
+                                </td>
+                                <td>
+                                        <select size="1" name="urgency">
+                                                <?php
+                                                        for( $i = 1; $i <= 8; $i++ )
+                                                        {
+                                                                echo "<option value=\"$i\"";
+                                                                if ( $new_ps_file_info_array[ 'urgency' ] == $i )
+                                                                {
+                                                                        echo " SELECTED ";
+                                                                }
+                                                                echo ">";
+                                                                if ( $i == 1 )
+                                                                {
+                                                                        echo "High";
+                                                                }
+                                                                else if ( $i == 5 )
+                                                                {
+                                                                        echo "Normal";
+                                                                }
+                                                                else if ( $i == 8 )
+                                                                {
+                                                                        echo "Low";
+                                                                }
+                                                                else
+                                                                {
+                                                                        echo "$i";
+                                                                }
+                                                                echo "</option>\n";
+                                                        }
+                                                        if ( $new_ps_file_info_array[ 'urgency' ] == "none" )
+                                                        {
+                                                                echo "<option value=\"none\" SELECTED >None</option>";
+                                                        }
+                                                        else
+                                                        {
+                                                                echo "<option value=\"none\" >None</option>";
+                                                        }
+                                                 ?>
+
+                                        </select>
+                                </td>
+                        </tr>
+
+                </table>
+                <br>
+                <input type="submit" value="Update!">
+
+
+        </form>
+
+        <br>
+        <br>
+        <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>     <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+        <br>
+        <br>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Photoshop_File_Info.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_File_Info.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_File_Info.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_File_Info.php	2005/01/20 10:34:38
@@ -0,0 +1,2498 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     Photoshop_File_Info.php
+*
+* Description:  Provides functions that mimic the way Photoshop reads and writes
+*               metadata in it's 'File Info' dialog
+*
+* Author:       Evan Hunter
+*
+* Date:         11/11/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.11
+* Changes:      1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*
+* URL:          http://electronics.ozhiker.com
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+// TODO: XMP sections: XAPMM, TIFF, EXIF
+
+
+include 'Toolkit_Version.php';          // Change: added as of version 1.11
+
+
+/******************************************************************************
+* Global Variable:      Software Name
+*
+* Contents:     The string that is appended to fields which store the name of
+*               the software editor.
+*
+******************************************************************************/
+
+$GLOBALS[ "Software Name" ] = "(PHP JPEG Metadata Toolkit v" . $GLOBALS['Toolkit_Version'] . ")";          // Change:  Changed version numbers to reference Toolkit_Version.php - as of version 1.11
+
+/******************************************************************************
+* End of Global Variable:     Software Name
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_photoshop_file_info
+*
+* Description:  Retrieves Photoshop 'File Info' metadata in the same way that Photoshop
+*               does. The results are returned in an array as below:
+*
+*               $file_info_array = array(
+*                       "title"                  => "",
+*                       "author"                 => "",
+*                       "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
+*                       "caption"                => "",
+*                       "captionwriter"          => "",
+*                       "jobname"                => "",      // Note: Not used in Photoshop CS
+*                       "copyrightstatus"        => "",
+*                       "copyrightnotice"        => "",
+*                       "ownerurl"               => "",
+*                       "keywords"               => array( 0 => "", 1 => "", ... ),
+*                       "category"               => "",     // Note: Max 3 characters
+*                       "supplementalcategories" => array( 0 => "", 1 => "", ... ),
+*                       "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
+*                       "city"                   => "",
+*                       "state"                  => "",
+*                       "country"                => "",
+*                       "credit"                 => "",
+*                       "source"                 => "",
+*                       "headline"               => "",
+*                       "instructions"           => "",
+*                       "transmissionreference"  => "",
+*                       "urgency"                => "" );
+*
+* Parameters:   Exif_array - an array containing the EXIF information to be
+*                            searched, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again)
+*               XMP_array - an array containing the XMP information to be
+*                           searched, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again)
+*               IRB_array - an array containing the Photoshop IRB information
+*                           to be searched, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again)
+*
+* Returns:      outputarray - an array as above, containing the Photoshop File Info data
+*
+******************************************************************************/
+
+function get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array )
+{
+
+        // Create a blank array to receive the output
+        $outputarray = array(
+                "title" => "",
+                "author" => "",
+                "authorsposition" => "",
+                "caption" => "",
+                "captionwriter" => "",
+                "jobname" => "",
+                "copyrightstatus" => "",
+                "copyrightnotice" => "",
+                "ownerurl" => "",
+                "keywords" => array(),
+                "category" => "",
+                "supplementalcategories" => array(),
+                "date" => "",
+                "city" => "",
+                "state" => "",
+                "country" => "",
+                "credit" => "",
+                "source" => "",
+                "headline" => "",
+                "instructions" => "",
+                "transmissionreference" => "",
+                "urgency" => "" );
+
+
+        /***************************************/
+
+        // XMP Processing
+
+
+        // Retrieve the dublin core section from the XMP header
+
+        // Extract the Dublin Core section from the XMP
+        $dublincore_block = find_XMP_block( $XMP_array, "dc" );
+
+        // Check that the Dublin Core section exists
+        if ( $dublincore_block != FALSE )
+        {
+                // Dublin Core Description Field contains caption
+                // Extract Description
+                $Item = find_XMP_item( $dublincore_block, "dc:description" );
+
+                // Check if Description Tag existed
+                if ( $Item != FALSE )
+                {
+                        // Ensure that the Description value exists and save it.
+                        if  ( ( array_key_exists( 'children', $Item ) ) &&
+                              ( $Item['children'][0]['tag'] == "rdf:Alt" ) &&
+                              ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'caption' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" );
+                        }
+                }
+
+                /***************************************/
+
+                // Dublin Core Creator Field contains author
+                // Extract Description
+                $Item = find_XMP_item( $dublincore_block, "dc:creator" );
+
+                // Check if Creator Tag existed
+                if ( $Item != FALSE )
+                {
+                        // Ensure that the Creator value exists and save it.
+                        if  ( ( array_key_exists( 'children', $Item ) ) &&
+                              ( $Item['children'][0]['tag'] =="rdf:Seq" ) &&
+                              ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "\n" );
+                        }
+                }
+
+                /***************************************/
+
+                // Dublin Core Title Field contains title
+                // Extract Title
+                $Item = find_XMP_item( $dublincore_block, "dc:title" );
+
+                // Check if Title Tag existed
+                if ( $Item != FALSE )
+                {
+                        // Ensure that the Title value exists and save it.
+                        if  ( ( array_key_exists( 'children', $Item ) ) &&
+                              ( $Item['children'][0]['tag'] =="rdf:Alt" ) &&
+                              ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
+                        {
+
+                                $outputarray = add_to_field( $outputarray, 'title' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," );
+                        }
+                }
+
+                /***************************************/
+
+                // Dublin Core Rights Field contains copyrightnotice
+                // Extract Rights
+                $Item = find_XMP_item( $dublincore_block, "dc:rights" );
+
+                // Check if Rights Tag existed
+                if ( $Item != FALSE )
+                {
+                        // Ensure that the Rights value exists and save it.
+                        if  ( ( array_key_exists( 'children', $Item ) ) &&
+                              ( $Item['children'][0]['tag'] =="rdf:Alt" ) &&
+                              ( array_key_exists( 'value', $Item['children'][0]['children'][0] ) ) )
+                        {
+
+                                $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['value'] ), "," );
+                        }
+                }
+
+                /***************************************/
+
+                // Dublin Core Subject Field contains keywords
+                // Extract Subject
+                $Item = find_XMP_item( $dublincore_block, "dc:subject" );
+
+                // Check if Subject Tag existed
+                if ( $Item != FALSE )
+                {
+                        // Ensure that the Subject values exist
+                        if  ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) )
+                        {
+                                // Cycle through each Subject value and save them
+                                foreach ( $Item['children'][0]['children'] as $keywords )
+                                {
+                                        if ( ! in_array ( HTML_UTF8_Escape( $keywords['value'] ), $outputarray['keywords']))
+                                        {
+                                                if  ( array_key_exists( 'value', $keywords ) )
+                                                {
+                                                        $outputarray['keywords'][] = HTML_UTF8_Escape( $keywords['value'] );
+                                                }
+                                        }
+                                }
+                        }
+                }
+
+
+        }
+
+        /***************************************/
+
+        // Find the Photoshop Information within the XMP block
+        $photoshop_block = find_XMP_block( $XMP_array, "photoshop" );
+
+        // Check that the Photoshop Information exists
+        if ( $photoshop_block != FALSE )
+        {
+                // The Photoshop CaptionWriter tag contains captionwriter - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" );
+
+                // Check that the CaptionWriter Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'captionwriter' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Headline tag contains headline - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Headline" );
+
+                // Check that the Headline Field exists and save the value
+                if ( ( $Item != FALSE )  && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'headline' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Instructions tag contains instructions - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Instructions" );
+
+                // Check that the Instructions Field exists and save the value
+                if ( ( $Item != FALSE )  && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'instructions' , HTML_UTF8_Escape( $Item['value'] ), "\n" );
+                }
+
+                /***************************************/
+
+                // The Photoshop AuthorsPosition tag contains authorsposition - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" );
+
+                // Check that the AuthorsPosition Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'authorsposition' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Credit tag contains credit - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Credit" );
+
+                // Check that the Credit Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'credit' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Source tag contains source - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Source" );
+
+                // Check that the Credit Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'source' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop City tag contains city - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:City" );
+
+                // Check that the City Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'city' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop State tag contains state - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:State" );
+
+                // Check that the State Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'state' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Country tag contains country - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Country" );
+
+                // Check that the Country Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'country' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop TransmissionReference tag contains transmissionreference - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" );
+
+                // Check that the TransmissionReference Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'transmissionreference' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Category tag contains category - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Category" );
+
+                // Check that the TransmissionReference Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'category' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop DateCreated tag contains date - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:DateCreated" );
+
+                // Check that the DateCreated Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'date' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop Urgency tag contains urgency - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:Urgency" );
+
+                // Check that the Urgency Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'urgency' , HTML_UTF8_Escape( $Item['value'] ), "," );
+                }
+
+                /***************************************/
+
+                // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it
+                $Item = find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" );
+
+                // Check that the SupplementalCategories Field exists
+                if ( $Item != FALSE )
+                {
+                        // Check that the values exist
+                        if  ( ( array_key_exists( 'children', $Item ) ) && ( $Item['children'][0]['tag'] =="rdf:Bag" ) )
+                        {
+                                // Cycle through the values and save them
+                                foreach ( $Item['children'][0]['children'] as $sup_category )
+                                {
+                                        if ( ( array_key_exists( 'value', $sup_category ) ) &&
+                                             ( ! in_array ( HTML_UTF8_Escape( $sup_category['value'] ), $outputarray['supplementalcategories'])) )
+                                        {
+                                                if ( array_key_exists( 'value', $sup_category ) )
+                                                {
+                                                        $outputarray['supplementalcategories'][] = HTML_UTF8_Escape( $sup_category['value'] );
+                                                }
+                                        }
+                                }
+                        }
+                }
+
+        }
+
+        /***************************************/
+
+        // Find the Job Reference Information within the XMP block
+        $job_block = find_XMP_block( $XMP_array, "xapBJ" );
+
+        // Check that the Job Reference Information exists
+        if ( $job_block != FALSE )
+        {
+                // The JobRef Field contains jobname - Find it
+                $Item = find_XMP_item( $job_block, "xapBJ:JobRef" );
+
+                // Check that the JobRef Field exists
+                if ( $Item != FALSE )
+                {
+                        // Check that the value exists and save it
+                        if ( ( array_key_exists( 'children', $Item ) ) &&
+                             ( $Item['children'][0]['tag'] =="rdf:Bag" ) &&
+                             ( array_key_exists( 'children', $Item['children'][0] ) ) &&
+                             ( $Item['children'][0]['children'][0]['tag'] =="rdf:li" ) &&
+                             ( array_key_exists( 'children', $Item['children'][0]['children'][0] ) ) &&
+                             ( $Item['children'][0]['children'][0]['children'][0]['tag'] =="stJob:name" ) &&
+                             ( array_key_exists( 'value', $Item['children'][0]['children'][0]['children'][0] ) ) )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'jobname' , HTML_UTF8_Escape( $Item['children'][0]['children'][0]['children'][0]['value'] ), "," );
+                        }
+                }
+        }
+
+
+        /***************************************/
+
+        // Find the Rights Information within the XMP block
+        $rights_block = find_XMP_block( $XMP_array, "xapRights" );
+
+        // Check that the Rights Information exists
+        if ( $rights_block != FALSE )
+        {
+                // The WebStatement Field contains ownerurl - Find it
+                $Item = find_XMP_item( $rights_block, "xapRights:WebStatement" );
+
+                // Check that the WebStatement Field exists and save the value
+                if ( ( $Item != FALSE )  && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'ownerurl' , HTML_UTF8_Escape( $Item['value'] ), "\n" );
+                }
+
+                /***************************************/
+
+                // The Marked Field contains copyrightstatus - Find it
+                $Item = find_XMP_item( $rights_block, "xapRights:Marked" );
+
+                // Check that the Marked Field exists and save the value
+                if ( ( $Item != FALSE ) && ( array_key_exists( 'value', $Item ) ) )
+                {
+                        if ( $Item['value'] == "True" )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," );
+                        }
+                        else
+                        {
+                                $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Public Domain", "," );
+                        }
+                }
+
+        }
+
+
+
+
+
+        /***************************************/
+
+        // Photoshop IRB Processing
+
+        // Check that the Photoshop IRB exists
+        if ( $IRB_array != FALSE )
+        {
+                // Create a translation table to convert carriage returns to linefeeds
+                $irbtrans = array("\x0d" => "\x0a");
+
+                // The Photoshop IRB Copyright flag (0x040A) contains copyrightstatus - find it
+                $IRB_copyright_flag = find_Photoshop_IRB_Resource( $IRB_array, 0x040A );
+
+                // Check if the Copyright flag Field exists, and save the value
+                if( $IRB_copyright_flag != FALSE )
+                {
+                        // Check the value of the copyright flag
+                        if ( hexdec( bin2hex( $IRB_copyright_flag['ResData'] ) ) == 1 )
+                        {
+                                // Save the value
+                                $outputarray = add_to_field( $outputarray, 'copyrightstatus' , "Copyrighted Work", "," );
+                        }
+                        else
+                        {
+                                // Do nothing - copyrightstatus will be set to unmarked if still blank at end
+                        }
+                }
+
+                /***************************************/
+
+                // The Photoshop IRB URL (0x040B) contains ownerurl - find it
+                $IRB_url = find_Photoshop_IRB_Resource( $IRB_array, 0x040B );
+
+                // Check if the URL Field exists and save the value
+                if( $IRB_url != FALSE )
+                {
+                        $outputarray = add_to_field( $outputarray, 'ownerurl' , strtr( $IRB_url['ResData'], $irbtrans ), "\n" );
+                }
+
+                /***************************************/
+
+                // Extract any IPTC block from the Photoshop IRB information
+                $IPTC_array = get_Photoshop_IPTC( $IRB_array );
+
+                // Check if the IPTC block exits
+                if ( ( $IPTC_array != FALSE ) && ( count( $IPTC_array ) != 0 ) )
+                {
+                        // The IPTC Caption/Abstract Field contains caption - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:120" );
+
+                        // Check if the Caption/Abstract Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'caption' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Caption Writer/Editor Field contains captionwriter - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:122" );
+
+                        // Check if the Caption Writer/Editor Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'captionwriter' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Headline Field contains headline - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:105" );
+
+                        // Check if the Headline Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'headline' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Special Instructions Field contains instructions - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:40" );
+
+                        // Check if the Special Instructions Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'instructions' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC By-Line Field contains author - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:80" );
+
+                        // Check if the By-Line Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'author' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC By-Line Title Field contains authorsposition - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:85" );
+
+                        // Check if the By-Line Title Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'authorsposition' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Credit Field contains credit - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:110" );
+
+                        // Check if the Credit Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'credit' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Source Field contains source - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:115" );
+
+                        // Check if the Source Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'source' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Object Name Field contains title - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:05" );
+
+                        // Check if the Object Name Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'title' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Date Created Field contains date - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:55" );
+
+                        // Check if the Date Created Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $date_array = unpack( "a4Year/a2Month/A2Day", $record['RecData'] );
+                                $tmpdate = $date_array['Year'] . "-" . $date_array['Month'] . "-" . $date_array['Day'];
+                                $outputarray = add_to_field( $outputarray, 'date' , strtr( $tmpdate, $irbtrans ), "," );
+
+                        }
+
+                        /***************************************/
+
+                        // The IPTC City Field contains city - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:90" );
+
+                        // Check if the City Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'city' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Province/State Field contains state - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:95" );
+
+                        // Check if the Province/State Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'state' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Country/Primary Location Name Field contains country - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:101" );
+
+                        // Check if the Country/Primary Location Name Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'country' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Original Transmission Reference Field contains transmissionreference - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:103" );
+
+                        // Check if the Original Transmission Reference Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'transmissionreference' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                        /***************************************/
+
+                        // The IPTC Category Field contains category - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:15" );
+
+                        // Check if the Category Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'category' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+
+                        /***************************************/
+
+                        // Cycle through the IPTC records looking for Supplemental Category records
+                        foreach ($IPTC_array as $record)
+                        {
+                                // Check if a Supplemental Category record has been found
+                                if ( $record['IPTC_Type'] == "2:20" )
+                                {
+                                        // A Supplemental Category record has been found, save it's value if the value doesn't already exist
+                                        if ( ! in_array ( $record['RecData'], $outputarray['supplementalcategories']))
+                                        {
+                                                $outputarray['supplementalcategories'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => "&#xA;") ) ;
+                                        }
+                                }
+                        }
+
+
+                        /***************************************/
+
+                        // The IPTC Urgency Field contains urgency - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:10" );
+
+                        // Check if the Urgency Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'urgency' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+
+
+                        /***************************************/
+
+                        // Cycle through the IPTC records looking for Keywords records
+                        foreach ($IPTC_array as $record)
+                        {
+                                // Check if a Keywords record has been found
+                                if ( $record['IPTC_Type'] == "2:25" )
+                                {
+                                        // A Keywords record has been found, save it's value if the value doesn't already exist
+                                        if ( ! in_array ( $record['RecData'], $outputarray['keywords']))
+                                        {
+                                                $outputarray['keywords'][] = strtr( $record['RecData'], array("\x0a" => "", "\x0d" => "&#xA;") ) ;
+                                        }
+                                }
+                        }
+
+
+                        /***************************************/
+
+                        // The IPTC Copyright Notice Field contains copyrightnotice - find it
+                        $record = find_IPTC_Resource( $IPTC_array, "2:116" );
+
+                        // Check if the Copyright Field exists and save the value
+                        if ( $record != FALSE  )
+                        {
+                                $outputarray = add_to_field( $outputarray, 'copyrightnotice' , strtr( $record['RecData'], $irbtrans ), "\n" );
+                        }
+
+                }
+        }
+
+
+
+
+        /***************************************/
+
+        // EXIF Processing
+
+
+        // Retreive Information from the EXIF data if it exists
+
+        if ( ( $Exif_array != FALSE ) || ( count( $Exif_array ) == 0 ) )
+        {
+                // Check the Image Description Tag - it can contain the caption
+                if ( array_key_exists( 270, $Exif_array[0] ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'caption' , $Exif_array[0][270]['Data'][0], "\n" );
+                }
+
+                /***************************************/
+
+                // Check the Copyright Information Tag - it contains the copyrightnotice
+                if ( array_key_exists( 33432, $Exif_array[0] ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'copyrightnotice' , HTML_UTF8_UnEscape( $Exif_array[0][33432]['Data'][0] ), "\n" );
+                }
+
+                /***************************************/
+
+                // Check the Artist Name Tag - it contains the author
+                if ( array_key_exists( 315, $Exif_array[0] ) )
+                {
+                        $outputarray = add_to_field( $outputarray, 'author' , HTML_UTF8_UnEscape( $Exif_array[0][315]['Data'][0] ), "\n" );
+                }
+
+        }
+
+
+        /***************************/
+
+        // FINISHED RETRIEVING INFORMATION
+
+        // Perform final processing
+
+
+        // Check if any urgency information was found
+        if ( $outputarray["urgency"] == "" )
+        {
+                // No urgency information was found - set it to default (None)
+                $outputarray["urgency"] = "none";
+        }
+
+        // Check if any copyrightstatus information was found
+        if ( $outputarray["copyrightstatus"] == "" )
+        {
+                // No copyrightstatus information was found - set it to default (Unmarked)
+                $outputarray["copyrightstatus"] = "unmarked";
+        }
+
+        // Return the resulting Photoshop File Info Array
+        return $outputarray;
+
+}
+
+/******************************************************************************
+* End of Function:     get_photoshop_file_info
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_photoshop_file_info
+*
+* Description:  Stores Photoshop 'File Info' metadata in the same way that Photoshop
+*               does. The 'File Info' metadata must be in an array similar to that
+*               returned by get_photoshop_file_info, as follows:
+*
+*               $file_info_array = array(
+*                       "title"                  => "",
+*                       "author"                 => "",
+*                       "authorsposition"        => "",      // Note: Not used in Photoshop 7 or higher
+*                       "caption"                => "",
+*                       "captionwriter"          => "",
+*                       "jobname"                => "",      // Note: Not used in Photoshop CS
+*                       "copyrightstatus"        => "",
+*                       "copyrightnotice"        => "",
+*                       "ownerurl"               => "",
+*                       "keywords"               => array( 0 => "", 1 => "", ... ),
+*                       "category"               => "",     // Note: Max 3 characters
+*                        "supplementalcategories" => array( 0 => "", 1 => "", ... ),
+*                       "date"                   => "",     // Note: DATE MUST BE IN YYYY-MM-DD format
+*                       "city"                   => "",
+*                       "state"                  => "",
+*                       "country"                => "",
+*                       "credit"                 => "",
+*                       "source"                 => "",
+*                       "headline"               => "",
+*                       "instructions"           => "",
+*                       "transmissionreference"  => "",
+*                       "urgency"                => "" );
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data. This contains the
+*                                  header information which is to be updated.
+*               new_ps_file_info_array - An array as above, which contains the
+*                                        'File Info' metadata information to be
+*                                        written.
+*               Old_Exif_array - an array containing the EXIF information to be
+*                                updated, as retrieved by get_EXIF_JPEG. (saves having to parse the EXIF again)
+*               Old_XMP_array - an array containing the XMP information to be
+*                               updated, as retrieved by read_XMP_array_from_text. (saves having to parse the XMP again)
+*               Old_IRB_array - an array containing the Photoshop IRB information
+*                                to be updated, as retrieved by get_Photoshop_IRB. (saves having to parse the IRB again)
+*
+* Returns:      jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data, containing the
+*                                  Photshop 'File Info' metadata. This can then
+*                                  be written to a file using put_jpeg_header_data.
+*
+******************************************************************************/
+
+function put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Old_Exif_array, $Old_XMP_array, $Old_IRB_array )
+{
+        /*******************************************/
+        // PREPROCESSING
+
+        // Check that the date is in the correct format (YYYY-MM-DD)
+
+        // Explode the date into pieces using the - symbol
+        $date_pieces = explode( "-", $new_ps_file_info_array[ 'date' ] );
+
+        // If there are not 3 pieces to the date, it is invalid
+        if ( count( $date_pieces ) != 3 )
+        {
+                // INVALID DATE
+                echo "Invalid Date - must be YYYY-MM-DD format<br>";
+                return FALSE;
+        }
+
+        // Cycle through each piece of the date
+        foreach( $date_pieces as $piece )
+        {
+                // If the piece is not numeric, then the date is invalid.
+                if ( ! is_numeric( $piece ) )
+                {
+                        // INVALID DATE
+                        echo "Invalid Date - must be YYYY-MM-DD format<br>";
+                        return FALSE;
+                }
+        }
+
+        // Make a unix timestamp at midnight on the date specified
+        $date_stamp = mktime( 0,0,0, $date_pieces[1], $date_pieces[2], $date_pieces[0] );
+
+
+
+
+        // Create a translation table to remove carriage return characters
+        $trans = array( "\x0d" => "" );
+
+        // Cycle through each of the File Info elements
+        foreach( $new_ps_file_info_array as $valkey => $val )
+        {
+                // If the element is 'Keywords' or 'Supplemental Categories', then
+                // it is an array, and needs to be treated as one
+                if ( ( $valkey != 'supplementalcategories' ) && ( $valkey != 'keywords' ) )
+                {
+                        // Not Keywords or Supplemental Categories
+                        // Convert escaped HTML characters to UTF8 and remove carriage returns
+                        $new_ps_file_info_array[ $valkey ] = strtr( HTML_UTF8_UnEscape( $val ), $trans );
+                }
+                else
+                {
+                        // Either Keywords or Supplemental Categories
+                        // Cycle through the array,
+                        foreach( $val as $subvalkey => $subval )
+                        {
+                                // Convert escaped HTML characters to UTF8 and remove carriage returns
+                                $new_ps_file_info_array[ $valkey ][ $subvalkey ] = strtr( HTML_UTF8_UnEscape( $subval ), $trans );
+                        }
+                }
+        }
+
+
+
+
+
+        /*******************************************/
+
+        // EXIF Processing
+
+
+        // Check if the EXIF array exists
+        if( $Old_Exif_array == FALSE )
+        {
+                // EXIF Array doesn't exist - create a new one
+                $new_Exif_array = array (       'Byte_Align' => "MM",
+                                                'Makernote_Tag' => false,
+                                                'Tags Name' => "TIFF",
+                                                 0 => array( "Tags Name" => "TIFF" ) );
+        }
+        else
+        {
+                // EXIF Array Does Exist - use it
+                $new_Exif_array = $Old_Exif_array;
+        }
+
+
+
+        // Update the EXIF Image Description Tag with the new value
+        $new_Exif_array[0][270] = array (       "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Name'],
+                                                "Tag Number" => 270,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 270 ]['Type'],
+                                                "Data"       => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ]) ));
+
+        // Update the EXIF Artist Name Tag with the new value
+        $new_Exif_array[0][315] = array (       "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Name'],
+                                                "Tag Number" => 315,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 315 ]['Type'],
+                                                "Data"       => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ) ) );
+
+        // Update the EXIF Copyright Information Tag with the new value
+        $new_Exif_array[0][33432] = array (     "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Name'],
+                                                "Tag Number" => 33432,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 33432 ]['Type'],
+                                                "Data"       => array( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ]) ) );
+
+
+        // Photoshop checks if the "Date and Time of Original" and "Date and Time when Digitized" tags exist
+        // If they don't exist, it means that the EXIF date may be wiped out if it is changed, so Photoshop
+        // copies the EXIF date to these two tags
+
+        if ( ( array_key_exists( 306, $new_Exif_array[0] ) )&&
+             ( array_key_exists( 34665, $new_Exif_array[0] ) ) &&
+             ( array_key_exists( 0, $new_Exif_array[0][34665] ) ) )
+        {
+                // Replace "Date and Time of Original" if it doesn't exist
+                if ( ! array_key_exists( 36867, $new_Exif_array[0][34665][0] ) )
+                {
+                        $new_Exif_array[0][34665][0][36867] = array (       "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Name'],
+                                                "Tag Number" => 36867,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36867 ]['Type'],
+                                                "Data"       => $new_Exif_array[0][306]['Data'] );
+                }
+
+                // Replace "Date and Time when Digitized" if it doesn't exist
+                if ( ! array_key_exists( 36868, $new_Exif_array[0][34665][0] ) )
+                {
+                        $new_Exif_array[0][34665][0][36868] = array (       "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Name'],
+                                                "Tag Number" => 36868,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['EXIF'][ 36868 ]['Type'],
+                                                "Data"       => $new_Exif_array[0][306]['Data'] );
+                }
+        }
+
+
+        // Photoshop changes the EXIF date Tag (306) to the current date, not the date that was entered in File Info
+        $exif_date = date ( "Y:m:d H:i:s" );
+
+        // Update the EXIF Date and Time Tag with the new value
+        $new_Exif_array[0][306] = array (       "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Name'],
+                                                "Tag Number" => 306,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 306 ]['Type'],
+                                                "Data"       => array( $exif_date ) );
+
+
+
+        // Photoshop replaces the EXIF Software or Firmware Tag with "Adobe Photoshop ..."
+        // This toolkit instead preserves existing value and appends the toolkit name to the end of it
+
+        // Check if the EXIF Software or Firmware Tag exists
+        if ( array_key_exists( 305, $new_Exif_array[0] ) )
+        {
+                // An existing EXIF Software or Firmware Tag was found
+                // Check if the existing Software or Firmware Tag already contains the Toolkit's name
+                if ( stristr ( $new_Exif_array[0][305]['Data'][0], $GLOBALS[ "Software Name" ]) == FALSE )
+                {
+                        // Toolkit Name string not found in the existing Software/Firmware string - append it.
+                        $firmware_str = $new_Exif_array[0][305]['Data'][0] . " " . $GLOBALS[ "Software Name" ];
+                }
+                else
+                {
+                        // Toolkit name already exists in Software/Firmware string - don't put another copy in the string
+                        $firmware_str = $new_Exif_array[0][305]['Data'][0];
+                }
+        }
+        else
+        {
+                // No Software/Firmware string exists - create one
+                $firmware_str = $GLOBALS[ "Software Name" ];
+        }
+
+        // Update the EXIF Software/Firmware Tag with the new value
+        $new_Exif_array[0][305] = array(        "Tag Name"   => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Name'],
+                                                "Tag Number" => 305,
+                                                "Data Type"  => 2,
+                                                "Type"       => $GLOBALS[ "IFD_Tag_Definitions" ]['TIFF'][ 305 ]['Type'],
+                                                "Data"       => array( HTML_UTF8_Escape( $firmware_str ) ) );
+
+
+
+
+
+        /*******************************************/
+
+        // Photoshop IRB Processing
+
+
+        // Check if there is an existing Photoshop IRB array
+        if ($Old_IRB_array == FALSE )
+        {
+                // No existing IRB array - create one
+                $new_IRB_array = array();
+        }
+        else
+        {
+                // There is an existing Photoshop IRB array - use it
+                $new_IRB_array = $Old_IRB_array;
+        }
+
+        // Remove any existing Copyright Flag, URL, or IPTC resources - these will be re-written
+        foreach( $new_IRB_array as  $resno => $res )
+        {
+                if ( ( $res[ 'ResID' ] == 0x040A ) ||
+                     ( $res[ 'ResID' ] == 0x040B ) ||
+                     ( $res[ 'ResID' ] == 0x0404 ) )
+                {
+                        array_splice( $new_IRB_array, $resno, 1 );
+                }
+        }
+
+
+        // Add a new Copyright Flag resource
+        if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" )
+        {
+                $PS_copyright_flag = "\x01"; // Copyrighted
+        }
+        else
+        {
+                $PS_copyright_flag = "\x00"; // Public domain or Unmarked
+        }
+        $new_IRB_array[] = array(       'ResID' => 0x040A,
+                                        'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040A],
+                                        'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040A],
+                                        'ResEmbeddedName' => "",
+                                        'ResData' => $PS_copyright_flag );
+
+
+
+        // Add a new URL resource
+        $new_IRB_array[] = array(       'ResID' => 0x040B,
+                                        'ResName' => $GLOBALS[ "Photoshop_ID_Names" ][0x040B],
+                                        'ResDesc' => $GLOBALS[ "Photoshop_ID_Descriptions" ][0x040B],
+                                        'ResEmbeddedName' => "",
+                                        'ResData' => $new_ps_file_info_array[ 'ownerurl' ] );
+
+
+
+        // Create IPTC resource
+
+        // IPTC requires date to be in the following format YYYYMMDD
+        $iptc_date = date( "Ymd", $date_stamp );
+
+        // Create the new IPTC array
+        $new_IPTC_array = array (
+                                  0 =>
+                                  array (
+                                    'IPTC_Type' => '2:00',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:00'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:00'],
+                                    'RecData' => "\x00\x02",
+                                  ),
+                                  1 =>
+                                  array (
+                                    'IPTC_Type' => '2:120',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:120'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:120'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'caption' ] ), 0 , 2000 ),
+                                  ),
+                                  2 =>
+                                  array (
+                                    'IPTC_Type' => '2:122',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:122'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:122'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'captionwriter' ] ), 0 , 32 ),
+                                  ),
+                                  3 =>
+                                  array (
+                                    'IPTC_Type' => '2:105',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:105'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:105'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'headline' ] ), 0 , 256 ),
+                                  ),
+                                  4 =>
+                                  array (
+                                    'IPTC_Type' => '2:40',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:40'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:40'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'instructions' ] ), 0, 256 ),
+                                  ),
+                                  5 =>
+                                  array (
+                                    'IPTC_Type' => '2:80',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:80'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:80'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'author' ] ), 0, 32 ),
+                                  ),
+                                  6 =>
+                                  array (
+                                    'IPTC_Type' => '2:85',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:85'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:85'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'authorsposition' ] ), 0, 32 ),
+                                  ),
+                                  7 =>
+                                  array (
+                                    'IPTC_Type' => '2:110',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:110'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:110'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'credit' ] ), 0, 32 ),
+                                  ),
+                                  8 =>
+                                  array (
+                                    'IPTC_Type' => '2:115',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:115'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:115'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'source' ] ), 0, 32 ),
+                                  ),
+                                  9 =>
+                                  array (
+                                    'IPTC_Type' => '2:05',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:05'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:05'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'title' ] ), 0, 64 ),
+                                  ),
+                                  10 =>
+                                  array (
+                                    'IPTC_Type' => '2:55',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:55'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:55'],
+                                    'RecData' => "$iptc_date",
+                                  ),
+                                  11 =>
+                                  array (
+                                    'IPTC_Type' => '2:90',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:90'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:90'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'city' ] ), 0, 32 ),
+                                  ),
+                                  12 =>
+                                  array (
+                                    'IPTC_Type' => '2:95',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:95'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:95'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'state' ] ), 0, 32 ),
+                                  ),
+                                  13 =>
+                                  array (
+                                    'IPTC_Type' => '2:101',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:101'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:101'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'country' ] ), 0, 64 ),
+                                  ),
+                                  14 =>
+                                  array (
+                                    'IPTC_Type' => '2:103',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:103'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:103'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'transmissionreference' ] ), 0, 32 ),
+                                  ),
+                                  15 =>
+                                  array (
+                                    'IPTC_Type' => '2:15',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:15'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:15'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'category' ] ), 0, 3 ),
+                                  ),
+                                  21 =>
+                                  array (
+                                    'IPTC_Type' => '2:116',
+                                    'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'],
+                                    'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'],
+                                    'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'copyrightnotice' ] ), 0, 128 ),
+                                  ),
+                                );
+
+        // Check the value of urgency is valid
+        if ( ( $new_ps_file_info_array[ 'urgency' ] > 0 ) && ( $new_ps_file_info_array[ 'urgency' ] < 9 ) )
+        {
+                // Add the Urgency item to the IPTC array
+                $new_IPTC_array[] = array (
+                                                'IPTC_Type' => '2:10',
+                                                'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:10'],
+                                                'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:10'],
+                                                'RecData' => substr( HTML_UTF8_Escape( $new_ps_file_info_array[ 'urgency' ] ), 0, 1 ),
+                                          );
+        }
+
+        // Cycle through the Supplemental Categories,
+        foreach( $new_ps_file_info_array[ 'supplementalcategories' ] as $supcat )
+        {
+                // Add this Supplemental Category to the IPTC array
+                $new_IPTC_array[] = array (
+                                            'IPTC_Type' => '2:20',
+                                            'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:20'],
+                                            'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:20'],
+                                            'RecData' => HTML_UTF8_Escape( $supcat ),
+                                          );
+        }
+
+
+        // Cycle through the Keywords,
+        foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword )
+        {
+                // Add this Keyword to the IPTC array
+                $new_IPTC_array[] = array (
+                                            'IPTC_Type' => '2:25',
+                                            'RecName' => $GLOBALS[ "IPTC_Entry_Names" ]['2:25'],
+                                            'RecDesc' => $GLOBALS[ "IPTC_Entry_Descriptions" ]['2:25'],
+                                            'RecData' => $keyword,
+                                          );
+        }
+
+
+        /***********************************/
+
+        // XMP Processing
+
+        // Check if XMP existed previously
+        if ($Old_XMP_array == FALSE )
+        {
+                // XMP didn't exist - create a new one based on a blank structure
+                $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], array( ) );
+        }
+        else
+        {
+                // XMP does exist
+                // Some old XMP processors used x:xapmeta, check for this
+                if ( $Old_XMP_array[0]['tag'] == 'x:xapmeta' )
+                {
+                        // x:xapmeta found - change it to x:xmpmeta
+                        $Old_XMP_array[0]['tag'] = 'x:xmpmeta';
+                }
+
+                // Ensure that the existing XMP has all required fields, and add any that are missing
+                $new_XMP_array = XMP_Check( $GLOBALS[ 'Blank XMP Structure' ], $Old_XMP_array );
+        }
+
+
+        // Process the XMP Photoshop block
+
+        // Find the Photoshop Information within the XMP block
+        $photoshop_block = & find_XMP_block( $new_XMP_array, "photoshop" );
+
+        // The Photoshop CaptionWriter tag contains captionwriter - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:CaptionWriter" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'captionwriter' ];
+
+        // The Photoshop Category tag contains category - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Category" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'category' ];
+
+        // The Photoshop DateCreated tag contains date - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:DateCreated" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'date' ];
+
+        // The Photoshop City tag contains city - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:City" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'city' ];
+
+        // The Photoshop State tag contains state - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:State" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'state' ];
+
+        // The Photoshop Country tag contains country - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Country" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'country' ];
+
+        // The Photoshop AuthorsPosition tag contains authorsposition - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:AuthorsPosition" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'authorsposition' ];
+
+        // The Photoshop Credit tag contains credit - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Credit" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'credit' ];
+
+        // The Photoshop Source tag contains source - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Source" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'source' ];
+
+        // The Photoshop Headline tag contains headline - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Headline" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'headline' ];
+
+        // The Photoshop Instructions tag contains instructions - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Instructions" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'instructions' ];
+
+        // The Photoshop TransmissionReference tag contains transmissionreference - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:TransmissionReference" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'transmissionreference' ];
+
+        // The Photoshop Urgency tag contains urgency - Find it and Update the value
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:Urgency" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'urgency' ];
+
+        // The Photoshop SupplementalCategories tag contains supplementalcategories - Find it
+        $Item = & find_XMP_item( $photoshop_block, "photoshop:SupplementalCategories" );
+
+        // Create an array to receive the XML list items for the Supplemental Categories
+        $new_supcat_array = array( );
+
+        // Cycle through the Supplemental Categories
+        foreach ( $new_ps_file_info_array[ 'supplementalcategories' ] as $sup_category )
+        {
+                // Add a new list item for this Supplemental Category
+                $new_supcat_array[] = array( 'tag' => 'rdf:li', 'value' => $sup_category );
+        }
+
+        // Add the array of Supplemental Category List Items to the Photoshop SupplementalCategories tag
+        $Item[ 'children' ][ 0 ][ 'children' ] = $new_supcat_array;
+
+
+
+        // Process the XMP XAP block
+
+        // Find the XAP Information within the XMP block
+        $XAP_block = & find_XMP_block( $new_XMP_array, "xap" );
+
+        // The XAP CreateDate tag contains date XMP was first created - Find it and Update the value
+        $Item = & find_XMP_item( $XAP_block, "xap:CreateDate" );
+
+        // Check if the CreateDate is blank
+        if ( $Item[ 'value' ] == "" )
+        {
+                // CreateDate is blank - we must have just added it - set it to the current date
+                $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
+                $Item[ 'value' ] .= get_Local_Timezone_Offset( );
+        }
+
+
+        // The XAP ModifyDate tag contains last resource change date  - Find it and Update the value to the current date
+        $Item = & find_XMP_item( $XAP_block, "xap:ModifyDate" );
+        $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
+        $Item[ 'value' ] .= get_Local_Timezone_Offset( );
+
+        // The XAP ModifyDate tag contains last XMP change date  - Find it and Update the value to the current date
+        $Item = & find_XMP_item( $XAP_block, "xap:MetadataDate" );
+        $Item[ 'value' ] = date( "Y-m-d\TH:i:s" );
+        $Item[ 'value' ] .= get_Local_Timezone_Offset( );
+
+
+
+        // The XAP CreatorTool tag contains name of the software editor  - Find it
+        $Item = & find_XMP_item( $XAP_block, "xap:CreatorTool" );
+
+        // Photoshop replaces the CreatorTool with "Adobe Photoshop ..."
+        // This toolkit instead preserves existing value and appends the toolkit name to the end of it
+
+        // Check if a CreatorTool already exists
+        if ( $Item[ 'value' ] != "" )
+        {
+                // An existing CreatorTool was found
+                // Check if the existing CreatorTool already contains the Toolkit's name
+                if ( stristr ( $Item[ 'value' ], $GLOBALS[ "Software Name" ]) == FALSE )
+                {
+                        // Toolkit Name string not found in the existing CreatorTool string - append it.
+                        $Item[ 'value' ] = $Item[ 'value' ] . " " . $GLOBALS[ "Software Name" ];
+                }
+                else
+                {
+                        // Toolkit name already exists in CreatorTool string - leave as is
+                }
+        }
+        else
+        {
+                // No CreatorTool string exists - create one
+                $Item[ 'value' ] = $GLOBALS[ "Software Name" ];
+        }
+
+
+
+
+        // Process the XMP Basic Job Information block
+
+        // Find the XAP Basic Job Information within the XMP block
+        $XAPBJ_block = & find_XMP_block( $new_XMP_array, "xapBJ" );
+
+        // The XAP Basic Job JobRef tag contains urgency - Find it and Update the value
+        $Item = & find_XMP_item( $XAPBJ_block, "xapBJ:JobRef" );
+        $Item[ 'children' ][ 0 ][ 'children' ] =
+                array( array (  'tag'        => 'rdf:li',
+                                'attributes' => array ( 'rdf:parseType' => 'Resource' ),
+                                'children'   => array ( 0 => array (    'tag'   => 'stJob:name',
+                                                                        'value' => $new_ps_file_info_array[ 'jobname' ] ),
+                                                      ),
+                             ),
+                     );
+
+
+
+
+        // Process the XMP XAP Rights Information block
+
+        // Find the XAP Rights Information within the XMP block
+        $XAPRights_block = & find_XMP_block( $new_XMP_array, "xapRights" );
+
+
+
+        // The XAP Rights Marked tag should only be present if copyrightstatus is 'Copyrighted Work' or 'Public Domain'
+        // If copyrightstatus 'Unmarked' or anything else, the XAP Rights Marked tag should be missing
+
+
+        // Remove any existing XAP Rights Marked tags - they will be replaced
+        foreach( $XAPRights_block as  $tagno => $tag )
+        {
+                if ( $tag[ 'tag' ] == "xapRights:Marked" )
+                {
+                        array_splice( $XAPRights_block, $tagno, 1 );
+                }
+        }
+
+        // Check the value of the copyrightstatus flag
+        if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Copyrighted Work" )
+        {
+                // Copyrighted - add the tag
+                $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'True' );
+        }
+        else if ( $new_ps_file_info_array[ 'copyrightstatus' ] == "Public Domain" )
+        {
+                // Public domain - add the tag
+                $XAPRights_block[] = array ( 'tag' => 'xapRights:Marked', 'value' => 'False' );
+        }
+        else
+        {
+                // Unmarked or Other - Do nothing - don't add a Marked tag
+        }
+
+
+        // The XAP Rights WebStatement tag contains ownerurl - Find it and Update the value
+        $Item = & find_XMP_item( $XAPRights_block, "xapRights:WebStatement" );
+        $Item[ 'value' ] = $new_ps_file_info_array[ 'ownerurl' ];
+
+
+
+
+        // Process the XMP Dublin Core block
+
+        // Find the Dublin Core Information within the XMP block
+        $DC_block = & find_XMP_block( $new_XMP_array, "dc" );
+
+
+        // The Dublin Core description tag contains caption - Find it and Update the value
+        $Item = & find_XMP_item( $DC_block, "dc:description" );
+        $Item[ 'children' ][ 0 ][ 'children' ] = array( array(  'tag'   => "rdf:li",
+                                                                'value' => $new_ps_file_info_array[ 'caption' ],
+                                                                'attributes' => array( 'xml:lang' => "x-default" ) ) );
+
+
+        // The Dublin Core title tag contains title - Find it and Update the value
+        $Item = & find_XMP_item( $DC_block, "dc:title" );
+        $Item[ 'children' ][ 0 ][ 'children' ] = array( array(  'tag'   => "rdf:li",
+                                                                'value' => $new_ps_file_info_array[ 'title' ],
+                                                                'attributes' => array( 'xml:lang' => "x-default" ) ) );
+
+
+        // The Dublin Core rights tag contains copyrightnotice - Find it and Update the value
+        $Item = & find_XMP_item( $DC_block, "dc:rights" );
+        $Item[ 'children' ][ 0 ][ 'children' ] = array( array(  'tag'   => "rdf:li",
+                                                                'value' => $new_ps_file_info_array[ 'copyrightnotice' ],
+                                                                'attributes' => array( 'xml:lang' => "x-default" ) ) );
+
+        // The Dublin Core creator tag contains author - Find it and Update the value
+        $Item = & find_XMP_item( $DC_block, "dc:creator" );
+        $Item[ 'children' ][ 0 ][ 'children' ] = array( array(  'tag'   => "rdf:li",
+                                                                'value' => $new_ps_file_info_array[ 'author' ]) );
+
+        // The Dublin Core subject tag contains keywords - Find it
+        $Item = & find_XMP_item( $DC_block, "dc:subject" );
+
+        // Create an array to receive the Keywords List Items
+        $new_keywords_array = array( );
+
+        // Cycle through each keyword
+        foreach( $new_ps_file_info_array[ 'keywords' ] as $keyword )
+        {
+                // Add a List item for this keyword
+                $new_keywords_array[] = array(  'tag'   => "rdf:li", 'value' => $keyword );
+        }
+        // Add the Keywords List Items array to the Dublin Core subject tag
+        $Item[ 'children' ][ 0 ][ 'children' ] = $new_keywords_array;
+
+
+
+        /***************************************/
+
+        // FINISHED UPDATING VALUES
+
+        // Insert the new IPTC array into the Photoshop IRB array
+        $new_IRB_array = put_Photoshop_IPTC( $new_IRB_array, $new_IPTC_array );
+
+        // Write the EXIF array to the JPEG header
+        $jpeg_header_data = put_EXIF_JPEG( $new_Exif_array, $jpeg_header_data );
+
+        // Convert the XMP array to XMP text
+        $xmp_text = write_XMP_array_to_text( $new_XMP_array );
+
+        // Write the XMP text to the JPEG Header
+        $jpeg_header_data = put_XMP_text( $jpeg_header_data, $xmp_text );
+
+        // Write the Photoshop IRB array to the JPEG header
+        $jpeg_header_data = put_Photoshop_IRB( $jpeg_header_data, $new_IRB_array );
+
+        return $jpeg_header_data;
+
+}
+
+/******************************************************************************
+* End of Function:     put_photoshop_file_info
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+*         INTERNAL FUNCTIONS
+*
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Local_Timezone_Offset
+*
+* Description:  Returns a string indicating the time difference between the local
+*               timezone and GMT in hours and minutes, e.g.  +10:00 or -06:30
+*
+* Parameters:   None
+*
+* Returns:      $tz_str - a string containing the timezone offset
+*
+******************************************************************************/
+
+function get_Local_Timezone_Offset( )
+{
+        // Retrieve the Timezone offset in seconds
+        $tz_seconds = date( "Z" );
+
+        // Check if the offset is less than zero
+        if ( $tz_seconds < 0 )
+        {
+                // Offset is less than zero - add a Minus sign to the output
+                $tz_str = "-";
+        }
+        else
+        {
+                // Offset is greater than or equal to zero - add a Plus sign to the output
+                $tz_str = "+";
+        }
+
+        // Add the absolute offset to the output, formatted as HH:MM
+        $tz_str .= gmdate( "H:i", abs($tz_seconds) );
+
+        // Return the result
+        return $tz_str;
+}
+
+/******************************************************************************
+* End of Function:     get_Local_Timezone_Offset
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     XMP_Check
+*
+* Description:  Checks a given XMP array against a reference array, and adds any
+*               missing blocks and tags
+*
+*               NOTE: This is a recursive function
+*
+* Parameters:   reference_array - The standard XMP array which contains all required tags
+*               check_array - The XMP array to check
+*
+* Returns:      output - a string containing the timezone offset
+*
+******************************************************************************/
+
+function XMP_Check( $reference_array, $check_array)
+{
+        // Cycle through each of the elements of the reference XMP array
+        foreach( $reference_array as $valkey => $val )
+        {
+
+                // Search for the current reference tag within the XMP array to be checked
+                $tagpos = find_XMP_Tag( $check_array,  $val );
+
+                // Check if the tag was found
+                if ( $tagpos === FALSE )
+                {
+                        // Tag not found - Add tag to array being checked
+                        $tagpos = count( $check_array );
+                        $check_array[ $tagpos ] = $val;
+                }
+
+                // Check if the reference tag has children
+                if ( array_key_exists( 'children', $val ) )
+                {
+                        // Reference tag has children - these need to be checked too
+
+                        // Determine if the array being checked has children for this tag
+                        if ( ! array_key_exists( 'children', $check_array[ $tagpos ] ) )
+                        {
+                                // Array being checked has no children - add a blank children array
+                                $check_array[ $tagpos ][ 'children' ] = array( );
+                        }
+
+                        // Recurse, checking the children tags against the reference children
+                        $check_array[ $tagpos ][ 'children' ] = XMP_Check( $val[ 'children' ] , $check_array[ $tagpos ][ 'children' ] );
+                }
+                else
+                {
+                        // No children - don't need to check anything else
+                }
+        }
+
+        // Return the checked XMP array
+        return $check_array;
+}
+
+
+/******************************************************************************
+* End of Function:     XMP_Check
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     find_XMP_Tag
+*
+* Description:  Searches one level of an XMP array for a specific tag, and
+*               returns the tag position. Does not descend the XMP tree.
+*
+* Parameters:   XMP_array - The XMP array which should be searched
+*               tag - The XMP tag to search for (in same format as would be found in XMP array)
+*
+* Returns:      output - a string containing the timezone offset
+*
+******************************************************************************/
+
+function find_XMP_Tag( $XMP_array, $tag )
+{
+        $namespacestr = "";
+
+        // Some tags have a namespace attribute which defines them (i.e. rdf:Description tags)
+
+        // Check if the tag being searched for has attributs
+        if ( array_key_exists( 'attributes', $tag ) )
+        {
+                // Tag has attributes - cycle through them
+                foreach( $tag['attributes'] as $key => $val )
+                {
+                        // Check if the current attribute is the namespace attribute - i.e. starts with xmlns:
+                        if ( strcasecmp( substr($key,0,6), "xmlns:" ) == 0 )
+                        {
+                                // Found a namespace attribute - save it for later.
+                                $namespacestr = $key;
+                        }
+                }
+        }
+
+
+
+        // Cycle through the elements of the XMP array to be searched.
+        foreach( $XMP_array as $valkey => $val )
+        {
+
+                // Check if the current element is a rdf:Description tag
+                if ( strcasecmp ( $tag[ 'tag' ], 'rdf:Description' ) == 0 )
+                {
+                        // Current element is a rdf:Description tag
+                        // Check if the namespace attribute is the same as in the tag that is being searched for
+                        if ( array_key_exists( $namespacestr, $val['attributes'] ) )
+                        {
+                                // Namespace is the same - this is the correct tag - return it's position
+                                return $valkey;
+                        }
+                }
+                // Otherwise check if the current element has the same name as the tag in question
+                else if ( strcasecmp ( $val[ 'tag' ], $tag[ 'tag' ] ) == 0 )
+                {
+                        // Tags have same name - this is the correct tag - return it's position
+                        return $valkey;
+                }
+        }
+
+        // Cycled through all tags without finding the correct one - return error value
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     find_XMP_Tag
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     create_GUID
+*
+* Description:  Creates a Globally Unique IDentifier, in the format that is used
+*               by XMP (and Windows). This value is not guaranteed to be 100% unique,
+*               but it is ridiculously unlikely that two identical values will be produced
+*
+* Parameters:   none
+*
+* Returns:      output - a string containing the timezone offset
+*
+******************************************************************************/
+
+function create_GUID( )
+{
+        // Create a md5 sum of a random number - this is a 32 character hex string
+        $raw_GUID = md5( uniqid( getmypid() . rand( ) . (double)microtime()*1000000, TRUE ) );
+
+        // Format the string into 8-4-4-4-12 (numbers are the number of characters in each block)
+        return  substr($raw_GUID,0,8) . "-" . substr($raw_GUID,8,4) . "-" . substr($raw_GUID,12,4) . "-" . substr($raw_GUID,16,4) . "-" . substr($raw_GUID,20,12);
+}
+
+/******************************************************************************
+* End of Function:     create_GUID
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     add_to_field
+*
+* Description:  Adds a value to a particular field in a Photoshop File Info array,
+*               first checking whether the value is already there. If the value is
+*               already in the array, it is not changed, otherwise the value is appended
+*               to whatever is already in that field of the array
+*
+* Parameters:   field_array - The Photoshop File Info array to receive the new value
+*               field - The File Info field which the value is for
+*               value - The value to be written into the File Info
+*               separator - The string to place between values when having to append the value
+*
+* Returns:      output - the Photoshop File Info array with the value added
+*
+******************************************************************************/
+
+function add_to_field( $field_array, $field, $value, $separator )
+{
+        // Check if the value is blank
+        if ( $value == "" )
+        {
+                // Value is blank - return File Info array unchanged
+                return $field_array;
+        }
+
+        // Check if the value can be found anywhere within the existing value for this field
+        if ( stristr ( $field_array[ $field ], $value ) == FALSE)
+        {
+                // Value could not be found
+                // Check if the existing value for the field is blank
+                if ( $field_array[$field] != "" )
+                {
+                        // Existing value for field is not blank - append a separator
+                        $field_array[$field] .= $separator;
+                }
+                // Append the value to the field
+                $field_array[$field] .= $value;
+        }
+
+        // Return the File Info Array
+        return $field_array;
+}
+
+/******************************************************************************
+* End of Function:     add_to_field
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     find_IPTC_Resource
+*
+* Description:  Searches an IPTC array for a particular record, and returns it if found
+*
+* Parameters:   IPTC_array - The IPTC array to search
+*               record_type - The IPTC record number to search for (e.g.  2:151 )
+*
+* Returns:      output - the contents of the record if found
+*               FALSE - otherwise
+*
+******************************************************************************/
+
+function find_IPTC_Resource( $IPTC_array, $record_type )
+{
+        // Cycle through the ITPC records
+        foreach ($IPTC_array as $record)
+        {
+                // Check the IPTC type against the required type
+                if ( $record['IPTC_Type'] == $record_type )
+                {
+                        // IPTC type matches - return this record
+                        return $record;
+                }
+        }
+
+        // No matching record found - return error code
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     find_IPTC_Resource
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     find_Photoshop_IRB_Resource
+*
+* Description:  Searches a Photoshop IRB array for a particular resource, and returns it if found
+*
+* Parameters:   IRB_array - The IRB array to search
+*               resource_ID - The IRB resource number to search for (e.g.  0x03F9 )
+*
+* Returns:      output - the contents of the resource if found
+*               FALSE - otherwise
+*
+******************************************************************************/
+
+function find_Photoshop_IRB_Resource( $IRB_array, $resource_ID )
+{
+        // Cycle through the IRB resources
+        foreach( $IRB_array as $IRB_Resource )
+        {
+                // Check the IRB resource ID against the required ID
+                if ( $resource_ID == $IRB_Resource['ResID'] )
+                {
+                        // Resource ID matches - return this resource
+                        return $IRB_Resource;
+                }
+        }
+
+        // No matching resource found - return error code
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     find_Photoshop_IRB_Resource
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     find_XMP_item
+*
+* Description:  Searches a one level of a XMP array for a particular item by name, and returns it if found.
+*               Does not descend through the XMP array
+*
+* Parameters:   Item_Array - The XMP array to search
+*               item_name - The name of the tag to serch for (e.g.  photoshop:CaptionWriter )
+*
+* Returns:      output - the contents of the tag if found
+*               FALSE - otherwise
+*
+******************************************************************************/
+
+function & find_XMP_item( & $Item_Array, $item_name )
+{
+        // Cycle through the top level of the XMP array
+        foreach( $Item_Array as $Item_Key => $Item )
+        {
+                // Check this tag name against the required tag name
+                if( $Item['tag'] == $item_name )
+                {
+                        // The tag names match - return the item
+                        return $Item_Array[ $Item_Key ];
+                }
+        }
+
+        // No matching tag found - return error code
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     find_XMP_item
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     find_XMP_block
+*
+* Description:  Searches a for a particular rdf:Description block within a XMP array, and returns its children if found.
+*
+* Parameters:   XMP_array - The XMP array to search as returned by read_XMP_array_from_text
+*               block_name - The namespace of the XMP block to be found (e.g.  photoshop or xapRights )
+*
+* Returns:      output - the children of the tag if found
+*               FALSE - otherwise
+*
+******************************************************************************/
+
+function & find_XMP_block( & $XMP_array, $block_name )
+{
+        // Check that the rdf:RDF section can be found (which contains the rdf:Description tags
+        if ( ( $XMP_array !== FALSE ) &&
+             ( ( $XMP_array[0]['tag'] ==  "x:xapmeta" ) ||
+               ( $XMP_array[0]['tag'] ==  "x:xmpmeta" ) ) &&
+             ( $XMP_array[0]['children'][0]['tag'] ==  "rdf:RDF" ) )
+        {
+                // Found rdf:RDF
+                // Make it's children easily accessible
+                $RDF_Contents = $XMP_array[0]['children'][0]['children'];
+
+                // Cycle through the children (rdf:Description tags)
+                foreach ($RDF_Contents as $RDF_Key => $RDF_Item)
+                {
+                        // Check if this is a rdf:description tag that has children
+                        if ( ( $RDF_Item['tag'] == "rdf:Description" ) &&
+                             ( array_key_exists( 'children', $RDF_Item ) ) )
+                        {
+                                // RDF Description tag has children,
+                                // Cycle through it's attributes
+                                foreach( $RDF_Item['attributes'] as $key => $val )
+                                {
+                                        // Check if this attribute matches the namespace block name required
+                                        if ( $key == "xmlns:$block_name" )
+                                        {
+                                                // Namespace matches required block name - return it's children
+                                                return  $XMP_array[0]['children'][0]['children'][ $RDF_Key ]['children'];
+                                        }
+                                }
+                        }
+                }
+        }
+
+        // No matching rdf:Description block found
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     find_XMP_block
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Blank XMP Structure
+*
+* Contents:     A template XMP array which can be used to create a new XMP segment
+*
+******************************************************************************/
+
+// Create a GUID to be used in this template array
+$new_GUID = create_GUID( );
+
+$GLOBALS[ 'Blank XMP Structure' ] =
+array (
+  0 =>
+  array (
+    'tag' => 'x:xmpmeta',
+    'attributes' =>
+    array (
+      'xmlns:x' => 'adobe:ns:meta/',
+      'x:xmptk' => 'XMP toolkit 3.0-28, framework 1.6',
+    ),
+    'children' =>
+    array (
+      0 =>
+      array (
+        'tag' => 'rdf:RDF',
+        'attributes' =>
+        array (
+          'xmlns:rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+          'xmlns:iX' => 'http://ns.adobe.com/iX/1.0/',
+        ),
+        'children' =>
+        array (
+          1 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:pdf' => 'http://ns.adobe.com/pdf/1.3/',
+            ),
+          ),
+          2 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:photoshop' => 'http://ns.adobe.com/photoshop/1.0/',
+            ),
+            'children' =>
+            array (
+              0 =>
+              array (
+                'tag' => 'photoshop:CaptionWriter',
+                'value' => '',
+              ),
+              1 =>
+              array (
+                'tag' => 'photoshop:Category',
+                'value' => '',
+              ),
+              2 =>
+              array (
+                'tag' => 'photoshop:DateCreated',
+                'value' => '',
+              ),
+              3 =>
+              array (
+                'tag' => 'photoshop:City',
+                'value' => '',
+              ),
+              4 =>
+              array (
+                'tag' => 'photoshop:State',
+                'value' => '',
+              ),
+              5 =>
+              array (
+                'tag' => 'photoshop:Country',
+                'value' => '',
+              ),
+              6 =>
+              array (
+                'tag' => 'photoshop:Credit',
+                'value' => '',
+              ),
+              7 =>
+              array (
+                'tag' => 'photoshop:Source',
+                'value' => '',
+              ),
+              8 =>
+              array (
+                'tag' => 'photoshop:Headline',
+                'value' => '',
+              ),
+              9 =>
+              array (
+                'tag' => 'photoshop:Instructions',
+                'value' => '',
+              ),
+              10 =>
+              array (
+                'tag' => 'photoshop:TransmissionReference',
+                'value' => '',
+              ),
+              11 =>
+              array (
+                'tag' => 'photoshop:Urgency',
+                'value' => '',
+              ),
+              12 =>
+              array (
+                'tag' => 'photoshop:SupplementalCategories',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Bag',
+                  ),
+                ),
+              ),
+              13 =>
+              array (
+                'tag' => 'photoshop:AuthorsPosition',
+                'value' => '',
+              ),
+            ),
+          ),
+          4 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:xap' => 'http://ns.adobe.com/xap/1.0/',
+            ),
+            'children' =>
+            array (
+              0 =>
+              array (
+                'tag' => 'xap:CreateDate',
+                'value' => '',
+              ),
+              1 =>
+              array (
+                'tag' => 'xap:ModifyDate',
+                'value' => '',
+              ),
+              2 =>
+              array (
+                'tag' => 'xap:MetadataDate',
+                'value' => '',
+              ),
+              3 =>
+              array (
+                'tag' => 'xap:CreatorTool',
+                'value' => '',
+              ),
+            ),
+          ),
+          5 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'about' => "uuid:$new_GUID",
+              'xmlns:stJob' => 'http://ns.adobe.com/xap/1.0/sType/Job#',
+              'xmlns:xapBJ' => 'http://ns.adobe.com/xap/1.0/bj/',
+            ),
+            'children' =>
+            array (
+              0 =>
+              array (
+                'tag' => 'xapBJ:JobRef',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Bag',
+                    'children' =>
+                    array (
+                    ),
+                  ),
+                ),
+              ),
+            ),
+          ),
+          6 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:xapRights' => 'http://ns.adobe.com/xap/1.0/rights/',
+            ),
+            'children' =>
+            array (
+              1 =>
+              array (
+                'tag' => 'xapRights:WebStatement',
+                'value' => '',
+              ),
+            ),
+          ),
+          7 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
+            ),
+            'children' =>
+            array (
+              0 =>
+              array (
+                'tag' => 'dc:format',
+                'value' => 'image/jpeg',
+              ),
+              1 =>
+              array (
+                'tag' => 'dc:title',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Alt',
+                  ),
+                ),
+              ),
+              2 =>
+              array (
+                'tag' => 'dc:description',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Alt',
+                  ),
+                ),
+              ),
+              3 =>
+              array (
+                'tag' => 'dc:rights',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Alt',
+                  ),
+                ),
+              ),
+              4 =>
+              array (
+                'tag' => 'dc:creator',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Seq',
+                  ),
+                ),
+              ),
+              5 =>
+              array (
+                'tag' => 'dc:subject',
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'rdf:Bag',
+                  ),
+                ),
+              ),
+            ),
+          ),
+
+/*          0 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:exif' => 'http://ns.adobe.com/exif/1.0/',
+            ),
+            'children' =>
+            array (
+
+//EXIF DATA GOES HERE - Not Implemented yet
+            ),
+          ),
+*/
+/*
+          2 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:tiff' => 'http://ns.adobe.com/tiff/1.0/',
+            ),
+            'children' =>
+            array (
+// TIFF DATA GOES HERE - Not Implemented yet
+              0 =>
+              array (
+                'tag' => 'tiff:Make',
+                'value' => 'NIKON CORPORATION',
+              ),
+            ),
+          ),
+*/
+/*
+          3 =>
+          array (
+            'tag' => 'rdf:Description',
+            'attributes' =>
+            array (
+              'rdf:about' => "uuid:$new_GUID",
+              'xmlns:stRef' => 'http://ns.adobe.com/xap/1.0/sType/ResourceRef#',
+              'xmlns:xapMM' => 'http://ns.adobe.com/xap/1.0/mm/',
+            ),
+            'children' =>
+            array (
+// XAPMM DATA GOES HERE - Not Implemented yet
+              0 =>
+              array (
+                'tag' => 'xapMM:DocumentID',
+                'value' => 'adobe:docid:photoshop:dceba4c2-e699-11d8-94b2-b6ec48319f2d',
+              ),
+              1 =>
+              array (
+                'tag' => 'xapMM:DerivedFrom',
+                'attributes' =>
+                array (
+                  'rdf:parseType' => 'Resource',
+                ),
+                'children' =>
+                array (
+                  0 =>
+                  array (
+                    'tag' => 'stRef:documentID',
+                    'value' => 'adobe:docid:photoshop:5144475b-e698-11d8-94b2-b6ec48319f2d',
+                  ),
+                  1 =>
+                  array (
+                    'tag' => 'stRef:instanceID',
+                    'value' => "uuid:$new_GUID",
+                  ),
+                ),
+              ),
+            ),
+          ),
+*/
+
+        ),
+      ),
+    ),
+  ),
+);
+
+
+
+/******************************************************************************
+* End of Global Variable:     Blank XMP Structure
+******************************************************************************/
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/TIFFExample.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/TIFFExample.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/TIFFExample.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/TIFFExample.php	2005/01/20 10:16:20
@@ -0,0 +1,121 @@
+<html>
+
+<!--***************************************************************************
+*
+* Filename:     TIFFExample.php
+*
+* Description:  An example of how the PHP JPEG Metadata Toolkit can be used to
+*               display TIFF Metadata.
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+***************************************************************************-->
+
+        <head>
+
+                <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+                <STYLE TYPE="text/css" MEDIA="screen, print, projection">
+                <!--
+
+                        BODY { background-color:#505050; color:#F0F0F0 }
+                        a  { color:orange  }
+                        .EXIF_Main_Heading { color:red }
+                        .EXIF_Secondary_Heading{ color: orange}
+                        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+                        .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;}
+
+                -->
+                </STYLE>
+
+
+                <?php
+                        // Turn off Error Reporting
+                        error_reporting ( 0 );
+
+                        // Hide any unknown EXIF tags
+                        $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE;
+
+                        include 'Toolkit_Version.php';
+                        include 'EXIF.php';
+
+                        // Retrieve the TIFF image filename from the http url request
+                        if ( ( !array_key_exists( 'tiff_fname', $GLOBALS['HTTP_GET_VARS'] ) ) ||
+                             ( $GLOBALS['HTTP_GET_VARS']['tiff_fname'] == "" ) )
+                        {
+                                echo "<title>No image filename defined</title>\n";
+                                echo "</head>\n";
+                                echo "<body>\n";
+                                echo "<p>No image filename defined - use GET method with field: tiff_fname</p>\n";
+                                echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n";         // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11
+                                echo "</body>\n";
+                                exit( );
+                        }
+                        else
+                        {
+                                $filename = $GLOBALS['HTTP_GET_VARS']['tiff_fname'];
+                        }
+
+
+                        // Output the title
+                        echo "<title>Metadata details for $filename</title>";
+
+
+
+                 ?>
+
+        </head>
+
+        <body>
+
+                <p >Interpreted using: <a href="http://www.ozhiker.com/electronics/pjmt/">PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>                <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+                <br>
+
+                <h2><B><U>Metadata for &quot;<?php echo $filename; ?>&quot;</U></B></h2>
+
+
+                <!-- Output the EXIF Information -->
+                <?php echo Interpret_EXIF_to_HTML( get_EXIF_TIFF( $filename ), $filename );  ?>
+
+                <BR>
+                <BR>
+                <BR>
+                <p>Interpreted using:</p>
+                <p><a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>              <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+
+        </body>
+
+</html>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/get_minolta_thumb.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/get_minolta_thumb.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/get_minolta_thumb.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/get_minolta_thumb.php	2004/08/05 16:10:18
@@ -0,0 +1,195 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     get_minolta_thumb.php
+*
+* Description:  This script extracts a Minolta EXIF Makernote Thumbnail
+*               from within a JPEG file and allows it to be displayed
+*
+* Usage:        get_minolta_thumb?filename=<filename>
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+        // Ensure that nothing can write to the standard io, before we get the header out
+        ob_start( );
+
+
+        include 'EXIF.php';
+
+
+        // retrieve the filename from the URL parameters
+
+        $filename = $GLOBALS['HTTP_GET_VARS']['filename'];
+
+        // Retrieve any EXIF data in the file
+
+        $Exif_array = get_EXIF_JPEG( $filename );
+
+
+        // Check if any EXIF data was retrieved
+        if ( $Exif_array === FALSE )
+        {
+                // No EXIF data was found - abort
+                ob_end_clean ( );
+                echo "<p>Error getting EXIF Information</p>\n";
+                return;
+        }
+
+
+        // Check that there is at least the Zeroth IFD in the array
+        if ( count( $Exif_array ) < 1  )
+        {
+                ob_end_clean ( );
+                echo "<p>Couldn't find TIFF IFD 0</p>\n";
+                return;
+        }
+        
+        
+
+        // Check that the EXIF IFD exists
+        if ( array_key_exists( 34665, $Exif_array[0] ) )
+        {
+                // Found the EXIF IFD,
+
+                // Check that the makernote tag exists
+                if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) )
+                {
+                        // Makernote Exists
+
+                        // Check that the Makernote is Olympus
+                        if  ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Olympus" )
+                        {
+                                // Makernote is Olympus
+                                // Check if an IFD exists for the makernote
+                                if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) )
+                                {
+                                        // Check if the Thumbnail tag 0x0088 exists
+                                        if ( array_key_exists( 0x0088, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) )
+                                        {
+                                                // Found a Thumbnail
+                                                // Get the data
+                                                $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0088]['Data'];
+                                                
+                                                // Sometimes the Minolta thumbnails are corrupt as there is no data
+                                                // Check that the data is OK
+                                                if ( $data !== FALSE )
+                                                {
+                                                        // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag
+                                                        // Restore the first byte of the jpeg thumbnail
+                                                        $data{0} = "\xff";
+
+                                                        // Display the thumbnail
+                                                        ob_end_clean ( );
+                                                        header("Content-type: image/jpeg");
+                                                        print $data;
+                                                }
+                                                else
+                                                {
+                                                        // Thumbnail data is missing - display message
+                                                        ob_end_clean ( );
+                                                        echo "<p>Thumbnail missing</p>\n";
+                                                }
+                                        }
+                                                // Check if the Thumbnail tag 0x0081 exists
+                                        else if ( array_key_exists( 0x0081, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) )
+                                        {
+                                                // Found a Thumbnail
+                                                // Get the data
+                                                $data = $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][0x0081]['Data'];
+
+                                                // Sometimes the Minolta thumbnails are corrupt as there is no data
+                                                // Check that the data is OK
+                                                if ( $data !== FALSE )
+                                                {
+                                                        // Minolta thumbnails seem to have the first byte incorrect - this could possibly be a counter in case the thumbnail needs to span more than one tag
+                                                        // Restore the first byte of the jpeg thumbnail
+                                                        $data{0} = "\xff";
+
+                                                        // Display the thumbnail
+                                                        ob_end_clean ( );
+                                                        header("Content-type: image/jpeg");
+                                                        print $data ;
+                                                }
+                                                else
+                                                {
+                                                        // Thumbnail data is missing - display message
+                                                        ob_end_clean ( );
+                                                        echo "<p>Thumbnail missing</p>\n";
+                                                }
+                                        }
+                                        else
+                                        {
+                                                // Couldn't find a Minolta thumbnail tag - display message
+                                                ob_end_clean ( );
+                                                echo "<p>Couldn't find Minolta Thumbnail Tag</p>\n";
+                                        }
+                                }
+                                else
+                                {
+                                        // Couldn't find an IFD in the Makernote tag - display message
+                                        ob_end_clean ( );
+                                        echo "<p>Makernote Doesn't contain IFD 0</p>\n";
+                                }
+
+                        }
+                        else
+                        {
+                                // Makernote does not use Olympus tags - display message
+                                ob_end_clean ( );
+                                echo "<p>Not an Olympus Makernote</p>\n";
+                        }
+                }
+                else
+                {
+                        // Couldn't find Makernote tag - display message
+                        ob_end_clean ( );
+                        echo "<p>Couldn't find Makernote</p>\n";
+                }
+        }
+        else
+        {
+                // Couldn't find the EXIF IFD - display message
+                ob_end_clean ( );
+                echo "<p>Couldn't find Exif IFD</p>\n";
+        }
+
+
+        return;
+        
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/JPEG.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/JPEG.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/JPEG.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/JPEG.php	2004/11/22 21:26:56
@@ -0,0 +1,973 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     JPEG.php
+*
+* Description:  Provides functions for reading and writing information to/from
+*               JPEG format files
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.10
+*
+* Changes:      1.00 -> 1.10 : changed put_jpeg_header_data to check if the data
+*                              being written exists
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_jpeg_header_data
+*
+* Description:  Reads all the JPEG header segments from an JPEG image file into an
+*               array
+*
+* Parameters:   filename - the filename of the file to JPEG file to read
+*
+* Returns:      headerdata - Array of JPEG header segments
+*               FALSE - if headers could not be read
+*
+******************************************************************************/
+
+function get_jpeg_header_data( $filename )
+{
+
+        // prevent refresh from aborting file operations and hosing file
+        ignore_user_abort(true);
+
+
+        // Attempt to open the jpeg file - the at symbol supresses the error message about
+        // not being able to open files. The file_exists would have been used, but it
+        // does not work with files fetched over http or ftp.
+        $filehnd = @fopen($filename, 'rb');
+
+        // Check if the file opened successfully
+        if ( ! $filehnd  )
+        {
+                // Could't open the file - exit
+                echo "<p>Could not open file $filename</p>\n";
+                return FALSE;
+        }
+
+
+        // Read the first two characters
+        $data = network_safe_fread( $filehnd, 2 );
+
+        // Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
+        if ( $data != "\xFF\xD8" )
+        {
+                // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
+                echo "<p>This probably is not a JPEG file</p>\n";
+                fclose($filehnd);
+                return FALSE;
+        }
+
+
+        // Read the third character
+        $data = network_safe_fread( $filehnd, 2 );
+
+        // Check that the third character is 0xFF (Start of first segment header)
+        if ( $data{0} != "\xFF" )
+        {
+                // NO FF found - close file and return - JPEG is probably corrupted
+                fclose($filehnd);
+                return FALSE;
+        }
+
+        // Flag that we havent yet hit the compressed image data
+        $hit_compressed_image_data = FALSE;
+
+
+        // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
+        //                                       2) we have hit the compressed image data (no more headers are allowed after data)
+        //                                       3) or end of file is hit
+
+        while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
+        {
+                // Found a segment to look at.
+                // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
+                if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
+                {
+                        // Segment isn't a Restart marker
+                        // Read the next two bytes (size)
+                        $sizestr = network_safe_fread( $filehnd, 2 );
+
+                        // convert the size bytes to an integer
+                        $decodedsize = unpack ("nsize", $sizestr);
+
+                        // Save the start position of the data
+                        $segdatastart = ftell( $filehnd );
+
+                        // Read the segment data with length indicated by the previously read size
+                        $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 );
+
+
+                        // Store the segment information in the output array
+                        $headerdata[] = array(  "SegType" => ord($data{1}),
+                                                "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ ord($data{1}) ],
+                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ ord($data{1}) ],
+                                                "SegDataStart" => $segdatastart,
+                                                "SegData" => $segdata );
+                }
+
+                // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
+                if ( $data{1} == "\xDA" )
+                {
+                        // Flag that we have hit the compressed image data - exit loop as no more headers available.
+                        $hit_compressed_image_data = TRUE;
+                }
+                else
+                {
+                        // Not an SOS - Read the next two bytes - should be the segment marker for the next segment
+                        $data = network_safe_fread( $filehnd, 2 );
+
+                        // Check that the first byte of the two is 0xFF as it should be for a marker
+                        if ( $data{0} != "\xFF" )
+                        {
+                                // NO FF found - close file and return - JPEG is probably corrupted
+                                fclose($filehnd);
+                                return FALSE;
+                        }
+                }
+        }
+
+        // Close File
+        fclose($filehnd);
+        // Alow the user to abort from now on
+        ignore_user_abort(false);
+
+        // Return the header data retrieved
+        return $headerdata;
+}
+
+
+/******************************************************************************
+* End of Function:     get_jpeg_header_data
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_jpeg_header_data
+*
+* Description:  Writes JPEG header data into a JPEG file. Takes an array in the
+*               same format as from get_jpeg_header_data, and combines it with
+*               the image data of an existing JPEG file, to create a new JPEG file
+*               WARNING: As this function will replace all JPEG headers,
+*                        including SOF etc, it is best to read the jpeg headers
+*                        from a file, alter them, then put them back on the same
+*                        file. If a SOF segment wer to be transfered from one
+*                        file to another, the image could become unreadable unless
+*                        the images were idenical size and configuration
+*
+*
+* Parameters:   old_filename - the JPEG file from which the image data will be retrieved
+*               new_filename - the name of the new JPEG to create (can be same as old_filename)
+*               jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      TRUE - on Success
+*               FALSE - on Failure
+*
+******************************************************************************/
+
+function put_jpeg_header_data( $old_filename, $new_filename, $jpeg_header_data )
+{
+
+        // Change: added check to ensure data exists, as of revision 1.10
+        // Check if the data to be written exists
+        if ( $jpeg_header_data == FALSE )
+        {
+                // Data to be written not valid - abort
+                return FALSE;
+        }
+
+        // extract the compressed image data from the old file
+        $compressed_image_data = get_jpeg_image_data( $old_filename );
+
+        // Check if the extraction worked
+        if ( ( $compressed_image_data === FALSE ) || ( $compressed_image_data === NULL ) )
+        {
+                // Couldn't get image data from old file
+                return FALSE;
+        }
+
+
+        // Cycle through new headers
+        foreach ($jpeg_header_data as $segno => $segment)
+        {
+                // Check that this header is smaller than the maximum size
+                if ( strlen($segment['SegData']) > 0xfffd )
+                {
+                        // Could't open the file - exit
+                        echo "<p>A Header is too large to fit in JPEG segment</p>\n";
+                        return FALSE;
+                }
+        }
+
+        ignore_user_abort(true);    ## prevent refresh from aborting file operations and hosing file
+
+
+        // Attempt to open the new jpeg file
+        $newfilehnd = @fopen($new_filename, 'wb');
+        // Check if the file opened successfully
+        if ( ! $newfilehnd  )
+        {
+                // Could't open the file - exit
+                echo "<p>Could not open file $new_filename</p>\n";
+                return FALSE;
+        }
+
+        // Write SOI
+        fwrite( $newfilehnd, "\xFF\xD8" );
+
+        // Cycle through new headers, writing them to the new file
+        foreach ($jpeg_header_data as $segno => $segment)
+        {
+
+                // Write segment marker
+                fwrite( $newfilehnd, sprintf( "\xFF%c", $segment['SegType'] ) );
+
+                // Write segment size
+                fwrite( $newfilehnd, pack( "n", strlen($segment['SegData']) + 2 ) );
+
+                // Write segment data
+                fwrite( $newfilehnd, $segment['SegData'] );
+        }
+
+        // Write the compressed image data
+        fwrite( $newfilehnd, $compressed_image_data );
+
+        // Write EOI
+        fwrite( $newfilehnd, "\xFF\xD9" );
+
+        // Close File
+        fclose($newfilehnd);
+
+        // Alow the user to abort from now on
+        ignore_user_abort(false);
+
+
+        return TRUE;
+
+}
+
+/******************************************************************************
+* End of Function:     put_jpeg_header_data
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     get_jpeg_Comment
+*
+* Description:  Retreives the contents of the JPEG Comment (COM = 0xFFFE) segment if one
+*               exists
+*
+* Parameters:   jpeg_header_data - the JPEG header data, as retrieved
+*                                  from the get_jpeg_header_data function
+*
+* Returns:      string - Contents of the Comment segement
+*               FALSE - if the comment segment couldnt be found
+*
+******************************************************************************/
+
+function get_jpeg_Comment( $jpeg_header_data )
+{
+        //Cycle through the header segments until COM is found or we run out of segments
+        $i = 0;
+        while ( ( $i < count( $jpeg_header_data) )  && ( $jpeg_header_data[$i]['SegName'] != "COM" ) )
+        {
+                $i++;
+        }
+
+        // Check if a COM segment has been found
+        if (  $i < count( $jpeg_header_data) )
+        {
+                // A COM segment was found, return it's contents
+                return $jpeg_header_data[$i]['SegData'];
+        }
+        else
+        {
+                // No COM segment found
+                return FALSE;
+        }
+}
+
+/******************************************************************************
+* End of Function:     get_jpeg_Comment
+******************************************************************************/
+
+
+/******************************************************************************
+*
+* Function:     put_jpeg_Comment
+*
+* Description:  Creates a new JPEG Comment segment from a string, and inserts
+*               this segment into the supplied JPEG header array
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data, into which the
+*                                  new Comment segment will be put
+*               $new_Comment - a string containing the new Comment
+*
+* Returns:      jpeg_header_data - the JPEG header data array with the new
+*                                  JPEG Comment segment added
+*
+******************************************************************************/
+
+function put_jpeg_Comment( $jpeg_header_data, $new_Comment )
+{
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an COM header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "COM" ) == 0 )
+                {
+                        // Found a preexisting Comment block - Replace it with the new one and return.
+                        $jpeg_header_data[$i]['SegData'] = $new_Comment;
+                        return $jpeg_header_data;
+                }
+        }
+
+
+
+        // No preexisting Comment block found, find where to put it by searching for the highest app segment
+        $i = 0;
+        while ( ( $i < count( $jpeg_header_data ) ) && ( $jpeg_header_data[$i]["SegType"] >= 0xE0 ) )
+        {
+                $i++;
+        }
+
+
+        // insert a Comment segment new at the position found of the header data.
+        array_splice($jpeg_header_data, $i , 0, array( array(   "SegType" => 0xFE,
+                                                                "SegName" => $GLOBALS[ "JPEG_Segment_Names" ][ 0xFE ],
+                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xFE ],
+                                                                "SegData" => $new_Comment ) ) );
+        return $jpeg_header_data;
+}
+
+/******************************************************************************
+* End of Function:     put_jpeg_Comment
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_Comment_to_HTML
+*
+* Description:  Generates html showing the contents of any JPEG Comment segment
+*
+* Parameters:   jpeg_header_data - the JPEG header data, as retrieved
+*                                  from the get_jpeg_header_data function
+*
+* Returns:      output - the HTML
+*
+******************************************************************************/
+
+function Interpret_Comment_to_HTML( $jpeg_header_data )
+{
+        // Create a string to receive the output
+        $output = "";
+
+        // read the comment segment
+        $comment = get_jpeg_Comment( $jpeg_header_data );
+
+        // Check if the comment segment was valid
+        if ( $comment !== FALSE )
+        {
+                // Comment exists - add it to the output
+                $output .= "<h2 class=\"JPEG_Comment_Main_Heading\">JPEG Comment</h2>\n";
+                $output .= "<p class=\"JPEG_Comment_Text\">$comment</p>\n";
+        }
+
+        // Return the result
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_Comment_to_HTML
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_jpeg_intrinsic_values
+*
+* Description:  Retreives information about the intrinsic characteristics of the
+*               jpeg image, such as Bits per Component, Height and Width.
+*
+* Parameters:   jpeg_header_data - the JPEG header data, as retrieved
+*                                  from the get_jpeg_header_data function
+*
+* Returns:      array - An array containing the intrinsic JPEG values
+*               FALSE - if the comment segment couldnt be found
+*
+******************************************************************************/
+
+function get_jpeg_intrinsic_values( $jpeg_header_data )
+{
+        // Create a blank array for the output
+        $Outputarray = array( );
+
+        //Cycle through the header segments until Start Of Frame (SOF) is found or we run out of segments
+        $i = 0;
+        while ( ( $i < count( $jpeg_header_data) )  && ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) != "SOF" ) )
+        {
+                $i++;
+        }
+
+        // Check if a SOF segment has been found
+        if ( substr( $jpeg_header_data[$i]['SegName'], 0, 3 ) == "SOF" )
+        {
+                // SOF segment was found, extract the information
+
+                $data = $jpeg_header_data[$i]['SegData'];
+
+                // First byte is Bits per component
+                $Outputarray['Bits per Component'] = ord( $data{0} );
+
+                // Second and third bytes are Image Height
+                $Outputarray['Image Height'] = ord( $data{ 1 } ) * 256 + ord( $data{ 2 } );
+
+                // Forth and fifth bytes are Image Width
+                $Outputarray['Image Width'] = ord( $data{ 3 } ) * 256 + ord( $data{ 4 } );
+
+                // Sixth byte is number of components
+                $numcomponents = ord( $data{ 5 } );
+
+                // Following this is a table containing information about the components
+                for( $i = 0; $i < $numcomponents; $i++ )
+                {
+                        $Outputarray['Components'][] = array (  'Component Identifier' => ord( $data{ 6 + $i * 3 } ),
+                                                                'Horizontal Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0xF0 ) / 16,
+                                                                'Vertical Sampling Factor' => ( ord( $data{ 7 + $i * 3 } ) & 0x0F ),
+                                                                'Quantization table destination selector' => ord( $data{ 8 + $i * 3 } ) );
+                }
+        }
+        else
+        {
+                // Couldn't find Start Of Frame segment, hence can't retrieve info
+                return FALSE;
+        }
+
+        return $Outputarray;
+}
+
+
+/******************************************************************************
+* End of Function:     get_jpeg_intrinsic_values
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_intrinsic_values_to_HTML
+*
+* Description:  Generates html showing some of the intrinsic JPEG values which
+*               were retrieved with the get_jpeg_intrinsic_values function
+*
+* Parameters:   values - the JPEG intrinsic values, as read from get_jpeg_intrinsic_values
+*
+* Returns:      OutputStr - A string containing the HTML
+*
+******************************************************************************/
+
+function Interpret_intrinsic_values_to_HTML( $values )
+{
+        // Check values are valid
+        if ( $values != FALSE )
+        {
+                // Write Heading
+                $OutputStr = "<h2 class=\"JPEG_Intrinsic_Main_Heading\">Intrinsic JPEG Information</h2>\n";
+
+                // Create Table
+                $OutputStr .= "<table class=\"JPEG_Intrinsic_Table\" border=1>\n";
+
+                // Put image height and width into table
+                $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Image Height</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Image Height'] . " pixels</td></tr>\n";
+                $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Image Width</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Image Width'] . " pixels</td></tr>\n";
+
+                // Put colour depth into table
+                if ( count( $values['Components'] ) == 1 )
+                {
+                        $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Colour Depth</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . $values['Bits per Component'] . " bit Monochrome</td></tr>\n";
+                }
+                else
+                {
+                        $OutputStr .= "<tr class=\"JPEG_Intrinsic_Table_Row\"><td class=\"JPEG_Intrinsic_Caption_Cell\">Colour Depth</td><td class=\"JPEG_Intrinsic_Value_Cell\">" . ($values['Bits per Component'] * count( $values['Components'] ) ) . " bit</td></tr>\n";
+                }
+
+                // Close Table
+                $OutputStr .= "</table>\n";
+
+                // Return html
+                return $OutputStr;
+        }
+}
+
+/******************************************************************************
+* End of Function:     Interpret_intrinsic_values_to_HTML
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_jpeg_image_data
+*
+* Description:  Retrieves the compressed image data part of the JPEG file
+*
+* Parameters:   filename - the filename of the JPEG file to read
+*
+* Returns:      compressed_data - A string containing the compressed data
+*               FALSE - if retrieval failed
+*
+******************************************************************************/
+
+function get_jpeg_image_data( $filename )
+{
+
+        // prevent refresh from aborting file operations and hosing file
+        ignore_user_abort(true);
+
+        // Attempt to open the jpeg file
+        $filehnd = @fopen($filename, 'rb');
+
+        // Check if the file opened successfully
+        if ( ! $filehnd  )
+        {
+                // Could't open the file - exit
+                return FALSE;
+        }
+
+
+        // Read the first two characters
+        $data = network_safe_fread( $filehnd, 2 );
+
+        // Check that the first two characters are 0xFF 0xDA  (SOI - Start of image)
+        if ( $data != "\xFF\xD8" )
+        {
+                // No SOI (FF D8) at start of file - close file and return;
+                fclose($filehnd);
+                return FALSE;
+        }
+
+
+
+        // Read the third character
+        $data = network_safe_fread( $filehnd, 2 );
+
+        // Check that the third character is 0xFF (Start of first segment header)
+        if ( $data{0} != "\xFF" )
+        {
+                // NO FF found - close file and return
+                fclose($filehnd);
+                return;
+        }
+
+        // Flag that we havent yet hit the compressed image data
+        $hit_compressed_image_data = FALSE;
+
+
+        // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
+        //                                       2) we have hit the compressed image data (no more headers are allowed after data)
+        //                                       3) or end of file is hit
+
+        while ( ( $data{1} != "\xD9" ) && (! $hit_compressed_image_data) && ( ! feof( $filehnd ) ))
+        {
+                // Found a segment to look at.
+                // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
+                if (  ( ord($data{1}) < 0xD0 ) || ( ord($data{1}) > 0xD7 ) )
+                {
+                        // Segment isn't a Restart marker
+                        // Read the next two bytes (size)
+                        $sizestr = network_safe_fread( $filehnd, 2 );
+
+                        // convert the size bytes to an integer
+                        $decodedsize = unpack ("nsize", $sizestr);
+
+                         // Read the segment data with length indicated by the previously read size
+                        $segdata = network_safe_fread( $filehnd, $decodedsize['size'] - 2 );
+                }
+
+                // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
+                if ( $data{1} == "\xDA" )
+                {
+                        // Flag that we have hit the compressed image data - exit loop after reading the data
+                        $hit_compressed_image_data = TRUE;
+
+                        // read the rest of the file in
+                        // Can't use the filesize function to work out
+                        // how much to read, as it won't work for files being read by http or ftp
+                        // So instead read 1Mb at a time till EOF
+
+                        $compressed_data = "";
+                        do
+                        {
+                                $compressed_data .= network_safe_fread( $filehnd, 1048576 );
+                        } while( ! feof( $filehnd ) );
+
+                        // Strip off EOI and anything after
+                        $EOI_pos = strpos( $compressed_data, "\xFF\xD9" );
+                        $compressed_data = substr( $compressed_data, 0, $EOI_pos );
+                }
+                else
+                {
+                        // Not an SOS - Read the next two bytes - should be the segment marker for the next segment
+                        $data = network_safe_fread( $filehnd, 2 );
+
+                        // Check that the first byte of the two is 0xFF as it should be for a marker
+                        if ( $data{0} != "\xFF" )
+                        {
+                                // Problem - NO FF foundclose file and return";
+                                fclose($filehnd);
+                                return;
+                        }
+                }
+        }
+
+        // Close File
+        fclose($filehnd);
+
+        // Alow the user to abort from now on
+        ignore_user_abort(false);
+
+
+        // Return the compressed data if it was found
+        if ( $hit_compressed_image_data )
+        {
+                return $compressed_data;
+        }
+        else
+        {
+                return FALSE;
+        }
+}
+
+
+/******************************************************************************
+* End of Function:     get_jpeg_image_data
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Generate_JPEG_APP_Segment_HTML
+*
+* Description:  Generates html showing information about the Application (APP)
+*               segments which are present in the JPEG file
+*
+* Parameters:   jpeg_header_data - the JPEG header data, as retrieved
+*                                  from the get_jpeg_header_data function
+*
+* Returns:      output - A string containing the HTML
+*
+******************************************************************************/
+
+function Generate_JPEG_APP_Segment_HTML( $jpeg_header_data )
+{
+        if ( $jpeg_header_data == FALSE )
+        {
+                return "";
+        }
+
+
+        // Write Heading
+        $output = "<h2 class=\"JPEG_APP_Segments_Main_Heading\">Application Metadata Segments</h2>\n";
+
+        // Create table
+        $output .= "<table class=\"JPEG_APP_Segments_Table\" border=1>\n";
+
+
+        // Cycle through each segment in the array
+
+        foreach( $jpeg_header_data as $jpeg_header )
+        {
+
+                // Check if the segment is a APP segment
+
+                if ( ( $jpeg_header['SegType'] >= 0xE0 ) && ( $jpeg_header['SegType'] <= 0xEF ) )
+                {
+                        // This is an APP segment
+
+                        // Read APP Segment Name - a Null terminated string at the start of the segment
+                        $seg_name = strtok($jpeg_header['SegData'], "\x00");
+
+                        // Some Segment names are either too long or not meaningfull, so
+                        // we should clean them up
+
+                        if ( $seg_name == "http://ns.adobe.com/xap/1.0/" )
+                        {
+                                $seg_name = "XAP/RDF (\"http://ns.adobe.com/xap/1.0/\")";
+                        }
+                        elseif ( $seg_name == "Photoshop 3.0" )
+                        {
+                                $seg_name = "Photoshop IRB (\"Photoshop 3.0\")";
+                        }
+                        elseif ( ( strncmp ( $seg_name, "[picture info]", 14) == 0 ) ||
+                                 ( strncmp ( $seg_name, "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) )
+                        {
+                                $seg_name = "[picture info]";
+                        }
+                        elseif (  strncmp ( $seg_name, "Type=", 5) == 0 )
+                        {
+                                $seg_name = "Epson Info";
+                        }
+                        elseif ( ( strncmp ( $seg_name, "HHHHHHHHHHHHHHH", 15) == 0 ) ||
+                                 ( strncmp ( $seg_name, "@s33", 5) == 0 ) )
+                        {
+                                $seg_name = "HP segment full of \"HHHHH\"";
+                        }
+
+
+                        // Clean the segment name so it doesn't cause problems with HTML
+                        $seg_name = htmlentities( $seg_name );
+
+                        // Output a Table row containing this APP segment
+                        $output .= "<tr class=\"JPEG_APP_Segments_Table_Row\"><td class=\"JPEG_APP_Segments_Caption_Cell\">$seg_name</td><td class=\"JPEG_APP_Segments_Type_Cell\">" . $jpeg_header['SegName'] . "</td><td  class=\"JPEG_APP_Segments_Size_Cell\" align=\"right\">" . strlen( $jpeg_header['SegData']). " bytes</td></tr>\n";
+                }
+        }
+
+        // Close the table
+        $output .= "</table>\n";
+
+        // Return the HTML
+        return $output;
+}
+
+
+/******************************************************************************
+* End of Function:     Generate_JPEG_APP_Segment_HTML
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     network_safe_fread
+*
+* Description:  Retrieves data from a file. This function is required since
+*               the fread function will not always return the requested number
+*               of characters when reading from a network stream or pipe
+*
+* Parameters:   file_handle - the handle of a file to read from
+*               length - the number of bytes requested
+*
+* Returns:      data - the data read from the file. may be less than the number
+*                      requested if EOF was hit
+*
+******************************************************************************/
+
+function network_safe_fread( $file_handle, $length )
+{
+        // Create blank string to receive data
+        $data = "";
+
+        // Keep reading data from the file until either EOF occurs or we have
+        // retrieved the requested number of bytes
+
+        while ( ( !feof( $file_handle ) ) && ( strlen($data) < $length ) )
+        {
+                $data .= fread( $file_handle, $length-strlen($data) );
+        }
+
+        // return the data read
+        return $data;
+}
+
+/******************************************************************************
+* End of Function:     network_safe_fread
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+* Global Variable:      JPEG_Segment_Names
+*
+* Contents:     The names of the JPEG segment markers, indexed by their marker number
+*
+******************************************************************************/
+
+$GLOBALS[ "JPEG_Segment_Names" ] = array(
+
+0xC0 =>  "SOF0",  0xC1 =>  "SOF1",  0xC2 =>  "SOF2",  0xC3 =>  "SOF4",
+0xC5 =>  "SOF5",  0xC6 =>  "SOF6",  0xC7 =>  "SOF7",  0xC8 =>  "JPG",
+0xC9 =>  "SOF9",  0xCA =>  "SOF10", 0xCB =>  "SOF11", 0xCD =>  "SOF13",
+0xCE =>  "SOF14", 0xCF =>  "SOF15",
+0xC4 =>  "DHT",   0xCC =>  "DAC",
+
+0xD0 =>  "RST0",  0xD1 =>  "RST1",  0xD2 =>  "RST2",  0xD3 =>  "RST3",
+0xD4 =>  "RST4",  0xD5 =>  "RST5",  0xD6 =>  "RST6",  0xD7 =>  "RST7",
+
+0xD8 =>  "SOI",   0xD9 =>  "EOI",   0xDA =>  "SOS",   0xDB =>  "DQT",
+0xDC =>  "DNL",   0xDD =>  "DRI",   0xDE =>  "DHP",   0xDF =>  "EXP",
+
+0xE0 =>  "APP0",  0xE1 =>  "APP1",  0xE2 =>  "APP2",  0xE3 =>  "APP3",
+0xE4 =>  "APP4",  0xE5 =>  "APP5",  0xE6 =>  "APP6",  0xE7 =>  "APP7",
+0xE8 =>  "APP8",  0xE9 =>  "APP9",  0xEA =>  "APP10", 0xEB =>  "APP11",
+0xEC =>  "APP12", 0xED =>  "APP13", 0xEE =>  "APP14", 0xEF =>  "APP15",
+
+
+0xF0 =>  "JPG0",  0xF1 =>  "JPG1",  0xF2 =>  "JPG2",  0xF3 =>  "JPG3",
+0xF4 =>  "JPG4",  0xF5 =>  "JPG5",  0xF6 =>  "JPG6",  0xF7 =>  "JPG7",
+0xF8 =>  "JPG8",  0xF9 =>  "JPG9",  0xFA =>  "JPG10", 0xFB =>  "JPG11",
+0xFC =>  "JPG12", 0xFD =>  "JPG13",
+
+0xFE =>  "COM",   0x01 =>  "TEM",   0x02 =>  "RES",
+
+);
+
+/******************************************************************************
+* End of Global Variable:     JPEG_Segment_Names
+******************************************************************************/
+
+
+/******************************************************************************
+* Global Variable:      JPEG_Segment_Descriptions
+*
+* Contents:     The descriptions of the JPEG segment markers, indexed by their marker number
+*
+******************************************************************************/
+
+$GLOBALS[ "JPEG_Segment_Descriptions" ] = array(
+
+/* JIF Marker byte pairs in JPEG Interchange Format sequence */
+0xC0 => "Start Of Frame (SOF) Huffman  - Baseline DCT",
+0xC1 =>  "Start Of Frame (SOF) Huffman  - Extended sequential DCT",
+0xC2 =>  "Start Of Frame Huffman  - Progressive DCT (SOF2)",
+0xC3 =>  "Start Of Frame Huffman  - Spatial (sequential) lossless (SOF3)",
+0xC5 =>  "Start Of Frame Huffman  - Differential sequential DCT (SOF5)",
+0xC6 =>  "Start Of Frame Huffman  - Differential progressive DCT (SOF6)",
+0xC7 =>  "Start Of Frame Huffman  - Differential spatial (SOF7)",
+0xC8 =>  "Start Of Frame Arithmetic - Reserved for JPEG extensions (JPG)",
+0xC9 =>  "Start Of Frame Arithmetic - Extended sequential DCT (SOF9)",
+0xCA =>  "Start Of Frame Arithmetic - Progressive DCT (SOF10)",
+0xCB =>  "Start Of Frame Arithmetic - Spatial (sequential) lossless (SOF11)",
+0xCD =>  "Start Of Frame Arithmetic - Differential sequential DCT (SOF13)",
+0xCE =>  "Start Of Frame Arithmetic - Differential progressive DCT (SOF14)",
+0xCF =>  "Start Of Frame Arithmetic - Differential spatial (SOF15)",
+0xC4 =>  "Define Huffman Table(s) (DHT)",
+0xCC =>  "Define Arithmetic coding conditioning(s) (DAC)",
+
+0xD0 =>  "Restart with modulo 8 count 0 (RST0)",
+0xD1 =>  "Restart with modulo 8 count 1 (RST1)",
+0xD2 =>  "Restart with modulo 8 count 2 (RST2)",
+0xD3 =>  "Restart with modulo 8 count 3 (RST3)",
+0xD4 =>  "Restart with modulo 8 count 4 (RST4)",
+0xD5 =>  "Restart with modulo 8 count 5 (RST5)",
+0xD6 =>  "Restart with modulo 8 count 6 (RST6)",
+0xD7 =>  "Restart with modulo 8 count 7 (RST7)",
+
+0xD8 =>  "Start of Image (SOI)",
+0xD9 =>  "End of Image (EOI)",
+0xDA =>  "Start of Scan (SOS)",
+0xDB =>  "Define quantization Table(s) (DQT)",
+0xDC =>  "Define Number of Lines (DNL)",
+0xDD =>  "Define Restart Interval (DRI)",
+0xDE =>  "Define Hierarchical progression (DHP)",
+0xDF =>  "Expand Reference Component(s) (EXP)",
+
+0xE0 =>  "Application Field 0 (APP0) - usually JFIF or JFXX",
+0xE1 =>  "Application Field 1 (APP1) - usually EXIF or XMP/RDF",
+0xE2 =>  "Application Field 2 (APP2) - usually Flashpix",
+0xE3 =>  "Application Field 3 (APP3)",
+0xE4 =>  "Application Field 4 (APP4)",
+0xE5 =>  "Application Field 5 (APP5)",
+0xE6 =>  "Application Field 6 (APP6)",
+0xE7 =>  "Application Field 7 (APP7)",
+
+0xE8 =>  "Application Field 8 (APP8)",
+0xE9 =>  "Application Field 9 (APP9)",
+0xEA =>  "Application Field 10 (APP10)",
+0xEB =>  "Application Field 11 (APP11)",
+0xEC =>  "Application Field 12 (APP12) - usually [picture info]",
+0xED =>  "Application Field 13 (APP13) - usually photoshop IRB / IPTC",
+0xEE =>  "Application Field 14 (APP14)",
+0xEF =>  "Application Field 15 (APP15)",
+
+
+0xF0 =>  "Reserved for JPEG extensions (JPG0)",
+0xF1 =>  "Reserved for JPEG extensions (JPG1)",
+0xF2 =>  "Reserved for JPEG extensions (JPG2)",
+0xF3 =>  "Reserved for JPEG extensions (JPG3)",
+0xF4 =>  "Reserved for JPEG extensions (JPG4)",
+0xF5 =>  "Reserved for JPEG extensions (JPG5)",
+0xF6 =>  "Reserved for JPEG extensions (JPG6)",
+0xF7 =>  "Reserved for JPEG extensions (JPG7)",
+0xF8 =>  "Reserved for JPEG extensions (JPG8)",
+0xF9 =>  "Reserved for JPEG extensions (JPG9)",
+0xFA =>  "Reserved for JPEG extensions (JPG10)",
+0xFB =>  "Reserved for JPEG extensions (JPG11)",
+0xFC =>  "Reserved for JPEG extensions (JPG12)",
+0xFD =>  "Reserved for JPEG extensions (JPG13)",
+
+0xFE =>  "Comment (COM)",
+0x01 =>  "For temp private use arith code (TEM)",
+0x02 =>  "Reserved (RES)",
+
+);
+
+/******************************************************************************
+* End of Global Variable:     JPEG_Segment_Descriptions
+******************************************************************************/
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/PictureInfo.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/PictureInfo.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/PictureInfo.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/PictureInfo.php	2004/08/08 11:24:00
@@ -0,0 +1,284 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     PictureInfo.php
+*
+* Description:  Provides functions for reading and writing information to/from
+*               the 'App 12' Picture Info segment of JPEG format files
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+include_once 'Unicode.php';
+
+/******************************************************************************
+*
+* Function:     get_jpeg_App12_Pic_Info
+*
+* Description:  Retrieves the Picture Info text information from an App12
+*               JPEG segment and returns it as a string. Uses information
+*               supplied by the get_jpeg_header_data function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      App12_Head - The text preceeding the Picture Info (often
+*                            the camera manufacturer's name)
+*               App12_Text - The Picture Info Text
+*               FALSE, FALSE - if an APP 12 Picture Info segment could not be found
+*
+******************************************************************************/
+
+function get_jpeg_App12_Pic_Info( $jpeg_header_data )
+{
+        // Flag that an APP12 segment has not been found yet
+        $App12_PI_Location = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // Check if we have found an APP12 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP12" ) == 0 )
+                {
+                        // Found an APP12 segment
+                        // Check if the APP12 has one of the correct labels (headers)
+                        // for a picture info segment
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP.  \00", 20) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert   \x00", 16) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) ||
+                             ( strncmp ( substr($jpeg_header_data[$i]['SegData'],1,3), "\x00\x00\x00", 3) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 )  )
+                        {
+                                // A Picture Info segment was found, mark this position
+                                $App12_PI_Location = $i;
+                        }
+                }
+        }
+
+        // Check if a Picture Info Segment was found
+        if ( $App12_PI_Location != -1 )
+        {
+                // A picture Info Segment was found - Process it
+
+                // Determine the length of the header if there is one
+                $head_length = 0;
+
+                if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "App12 Gevaert   \x00", 16) == 0 )
+                {
+                        $head_length = 16;
+                }
+                else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 )
+                {
+                        $head_length = 25;
+                }
+                else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "SEIKO EPSON CORP.  \00", 20) == 0 )
+                {
+                        $head_length = 20;
+                }
+                else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 )
+                {
+                        $head_length = 5;
+                }
+                else if ( strncmp ( substr($jpeg_header_data[$App12_PI_Location]['SegData'],1,3), "\x00\x00\x00", 3) == 0 ) // HP
+                {
+                        $head_length = 0;
+                }
+                else if ( strncmp ( $jpeg_header_data[$App12_PI_Location]['SegData'], "SanyoElectricDSC\x00", 17) == 0 )
+                {
+                        $head_length = 17;
+                }
+                else
+                {
+                        $head_length = 0;
+                }
+
+                // Extract the header and the Picture Info Text from the APP12 segment
+                $App12_PI_Head = substr( $jpeg_header_data[$App12_PI_Location]['SegData'], 0, $head_length );
+                $App12_PI_Text = substr( $jpeg_header_data[$App12_PI_Location]['SegData'], $head_length );
+
+                
+                // Return the text which was extracted
+
+                if ( ($pos = strpos ( $App12_PI_Text, "[end]" ) ) !== FALSE )
+                {
+                        return array( "Header" => $App12_PI_Head, "Picture Info" => substr( $App12_PI_Text, 0, $pos + 5 ) );
+                }
+                else
+                {
+                        return array( "Header" => $App12_PI_Head, "Picture Info" => $App12_PI_Text );
+                }
+        }
+
+        // No Picture Info Segment Found - Return False
+        return array( FALSE, FALSE );
+}
+
+/******************************************************************************
+* End of Function:     get_jpeg_header_data
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_jpeg_App12_Pic_Info
+*
+* Description:  Writes Picture Info text into an App12 JPEG segment. Uses information
+*               supplied by the get_jpeg_header_data function. If no App12 exists
+*               already a new one is created, otherwise it replaces the old one
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*               new_Pic_Info_Text - The Picture Info Text, including any header
+*                                   that is required
+*
+* Returns:      jpeg_header_data - the JPEG header array with the new Picture
+*                                  info segment inserted
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function put_jpeg_App12_Pic_Info( $jpeg_header_data, $new_Pic_Info_Text )
+{
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // Check if we have found an APP12 header,
+                if ( strcmp ( $jpeg_header_data[$i][SegName], "APP12" ) == 0 )
+                {
+                        // Found an APP12 segment
+                        // Check if the APP12 has one of the correct labels (headers)
+                        // for a picture info segment
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "[picture info]", 14) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "\x0a\x09\x09\x09\x09[picture info]", 19) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "SEIKO EPSON CORP.  \x00", 20) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Agfa Gevaert   \x00", 16) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "SanyoElectricDSC\x00", 17) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Type=", 5) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "OLYMPUS OPTICAL CO.,LTD.", 24) == 0 )  )
+                        {
+                                // Found a preexisting Picture Info segment - Replace it with the new one and return.
+                                $jpeg_header_data[$i][SegData] = $new_Pic_Info_Text;
+                                return $jpeg_header_data;
+                        }
+                }
+        }
+
+        // No preexisting Picture Info segment found, insert a new one at the start of the header data.
+
+        // Determine highest position of an APP segment at or below APP12, so we can put the
+        // new APP12 at this position
+        
+
+        $highest_APP = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // Check if we have found an APP segment at or below APP12,
+                if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xEC ) )
+                {
+                        // Found an APP segment at or below APP12
+                        $highest_APP = $i;
+                }
+        }
+
+        // Insert the new Picture Info segment
+        array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array(     "SegType" => 0xEC,
+                                                                                "SegName" => "APP12",
+                                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xEC ],
+                                                                                "SegData" => $new_Pic_Info_Text ) ) );
+
+        return $jpeg_header_data;
+
+
+}
+
+/******************************************************************************
+* End of Function:     put_jpeg_header_data
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_App12_Pic_Info_to_HTML
+*
+* Description:  Generates html showing the contents of any JPEG App12 Picture
+*               Info segment
+*
+* Parameters:   jpeg_header_data - the JPEG header data, as retrieved
+*                                  from the get_jpeg_header_data function
+*
+* Returns:      output - the HTML
+*
+******************************************************************************/
+
+function Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data )
+{
+        // Create a string to receive the output
+        $output = "";
+
+        // read the App12 Picture Info segment
+        $PI = get_jpeg_App12_Pic_Info( $jpeg_header_data );
+
+        // Check if the Picture Info segment was valid
+        if ( $PI !== array(FALSE, FALSE) )
+        {
+                // Picture Info exists - add it to the output
+                $output .= "<h2 class=\"Picture_Info_Main_Heading\">Picture Info Text</h2>\n";
+                $output .= "<p><span class=\"Picture_Info_Caption_Text\">Header: </span><span class=\"Picture_Info_Value_Text\">" . HTML_UTF8_Escape( $PI['Header'] ) . "</span></p>\n";
+                $output .= "<p class=\"Picture_Info_Caption_Text\">Picture Info Text:</p><pre class=\"Picture_Info_Value_Text\">" . HTML_UTF8_Escape( $PI['Picture Info'] ) . "</pre>\n";
+        }
+
+        // Return the result
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_App12_Pic_Info_to_HTML
+******************************************************************************/
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/EXIF_Tags.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Tags.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Tags.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Tags.php	2005/01/20 10:09:18
@@ -0,0 +1,915 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     EXIF_Tags.php
+*
+* Description:  Provides definitions of the tags for TIFF, EXIF, Interoperability,
+*               GPS, Meta, Kodak Special Effects and Kodak Borders IFD's.
+*
+* Author:       Evan Hunter
+*
+* Date:         1/8/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.11 : Added TIFF compression types ZIP, LZW and JPEG
+*                              Added embedded XMP tag
+*                              Added embedded Photoshop IRB tag
+*                              Fixed GPS tags after testing
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Tag_Definitions
+*
+* Contents:     This array defines the fields for the TIFF, EXIF, Interoperability,
+*               GPS, Meta, Kodak Special Effects and Kodak Borders IFD's.
+*               It is indexed by the IFD Type, then the Tag number
+*
+******************************************************************************/
+
+$GLOBALS[ "IFD_Tag_Definitions" ] = array(
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* TIFF Tags                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+
+"TIFF" => array(
+
+
+256 => array(   'Name'  => "Image Width",
+                'Description' => "Width of image in pixels (number of columns)",
+                'Type'  => "Numeric",
+                'Units' => "pixels" ),
+
+257 => array(   'Name'  =>  "Image Length",
+                'Description' => "Height of image in pixels (number of rows)",
+                'Type'  => "Numeric",
+                'Units' => "pixels" ),
+
+258 => array(   'Name'  => "Bits Per Sample",
+                'Description' => "Number of bits recorded per sample (a sample is usually one colour (Red, Green or Blue) of one pixel)",
+                'Type'  => "Numeric",
+                'Units' => "bits ( for each colour component )" ),
+
+
+259 => array(   'Name' => "Compression",
+                'Description' => "Specifies what type of compression is used 1 = uncompressed, 6 = JPEG compression (thumbnails only), Other = reserved",
+                'Type' => "Lookup",
+                1 => "Uncompressed",
+                5 => "LZW Compression",
+                6 => "Thumbnail compressed with JPEG compression",
+                7 => "JPEG Compression",
+                8 => "ZIP Compression" ),                                // Change: Added TIFF compression types as of version 1.11
+
+262 => array(   'Name' =>  "Photometric Interpretation",
+                'Description' => "Specifies Pixel Composition - 0 or 1 = monochrome, 2 = RGB, 3 = Palatte Colour, 4 = Transparency Mask, 6 = YCbCr",
+                'Type' => "Lookup",
+                2 => "RGB (Red Green Blue)",
+                6 => "YCbCr (Luminance, Chroma minus Blue, and Chroma minus Red)" ),
+
+274 => array(   'Name' =>  "Orientation",
+                'Description' => "Specifies the orientation of the image.\n
+1 = Row 0 top, column 0 left\n
+2 = Row 0 top, column 0 right\n
+3 = Row 0 bottom, column 0 right\n
+4 = Row 0 bottom, column 0 left\n
+5 = Row 0 left, column 0 top\n
+6 = Row 0 right, column 0 top\n
+7 = Row 0 right, column 0 bottom\n
+8 = Row 0 left, column 0 bottom",
+                'Type' => "Lookup",
+                1 => "No Rotation, No Flip \n(Row 0 is at the visual top of the image,\n and column 0 is the visual left-hand side)",
+                2 => "No Rotation, Flipped Horizontally \n(Row 0 is at the visual top of the image,\n and column 0 is the visual right-hand side)",
+                3 => "Rotated 180 degrees, No Flip \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual right-hand side)",
+                4 => "No Rotation, Flipped Vertically \n(Row 0 is at the visual bottom of the image,\n and column 0 is the visual left-hand side)",
+                5 => "Flipped Horizontally, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual top)",
+                6 => "No Flip, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual top)",
+                7 => "Flipped Horizontally, Rotated 90 degrees clockwise \n(Row 0 is at the visual right-hand side of of the image,\n and column 0 is the visual bottom)",
+                8 => "No Flip, Rotated 90 degrees counter clockwise \n(Row 0 is at the visual left-hand side of of the image,\n and column 0 is the visual bottom)" ),
+277 => array(   'Name' =>  "Samples Per Pixel",
+                'Description' => "Number of recorded samples (colours) per pixel - usually 1 for B&W, grayscale, and palette-colour, usually 3 for RGB and YCbCr",
+                'Type' => "Numeric",
+                'Units' => "Components (colours)" ),
+
+284 => array(   'Name' =>  "Planar Configuration",
+                'Description' => "Specifies whether pixel components are recorded in chunky or planar format - 1 = Chunky, 2 = Planar",
+                'Type' => "Lookup",
+                1 => "Chunky Format",
+                2 => "Planar Format" ),
+
+530 => array(   'Name' =>  "YCbCr Sub-Sampling",
+                'Description' => "Specifies ratio of chrominance to luminance components - [2, 1] = YCbCr4:2:2,  [2, 2] = YCbCr4:2:0",
+                'Type' => "Special" ),
+
+
+531 => array(   'Name' =>  "YCbCr Positioning",
+                'Description' => "Specifies location of chrominance and luminance components - 1 = centered, 2 = co-sited",
+                'Type' => "Lookup",
+                1 => "Chrominance components Centred in relation to luminance components",
+                2 => "Chrominance and luminance components Co-Sited" ),
+
+
+282 => array(   'Name' =>  "X Resolution",
+                'Description' => "Number of columns (pixels) per \'ResolutionUnit\'",
+                'Type' => "Numeric",
+                'Units'=> "pixels per 'Resolution Unit' " ),
+
+283 => array(   'Name' =>  "Y Resolution",
+                'Description' => "Number of rows (pixels) per \'ResolutionUnit\'",
+                'Type' => "Numeric",
+                'Units'=> "pixels per 'Resolution Unit' " ),
+
+296 => array(   'Name' =>  "Resolution Unit",
+                'Description' => "Units for measuring XResolution and YResolution - 1 = No units, 2 = Inches, 3 = Centimetres",
+                'Type' => "Lookup",
+                2 => "Inches",
+                3 => "Centimetres" ),
+
+273 => array(   'Name' =>  "Strip Offsets",
+                'Type' => "Numeric",
+                'Units'=> "bytes offset" ),
+
+278 => array(   'Name' =>  "Rows Per Strip",
+                'Type' => "Numeric",
+                'Units'=> "rows" ),
+
+279 => array(   'Name' => "Strip Byte Counts",
+                'Type' => "Numeric",
+                'Units'=> "bytes" ),
+
+513 => array(   'Name' => "Exif Thumbnail (JPEG Interchange Format)",
+                'Type' => "Special" ),
+
+514 => array(   'Name' => "Exif Thumbnail Length (JPEG Interchange Format Length)",
+                'Type' => "Numeric",
+                'Units'=> "bytes" ),
+
+301 => array(   'Name' => "Transfer Function",
+                'Type' => "Numeric",
+                'Units'=> "" ),
+
+318 => array(   'Name' => "White Point Chromaticity",
+                'Type' => "Numeric",
+                'Units'=> "(x,y coordinates on a 1931 CIE xy chromaticity diagram)" ),
+
+319 => array(   'Name' => "Primary Chromaticities",
+                'Type' => "Numeric",
+                'Units'=> "(Red x,y, Green x,y, Blue x,y coordinates on a 1931 CIE xy chromaticity diagram)" ),
+
+529 => array(   'Name' => "YCbCr Coefficients",
+                'Description' => "Transform Coefficients for transformation from RGB to YCbCr",
+                'Type' => "Numeric",
+                'Units'=> "(LumaRed, LumaGreen, LumaBlue [proportions of red, green, and blue in luminance])" ),
+
+532 => array(   'Name' => "Reference Black point and White point",
+                'Type' => "Numeric",
+                'Units'=> "(R or Y White Headroom, R or Y Black Footroom, G or Cb White Headroom, G or Cb Black Footroom, B or Cr White Headroom, B or Cr Black Footroom)" ),
+
+306 => array(   'Name' => "Date and Time",
+                'Type' => "Numeric",
+                'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ),
+
+270 => array(   'Name' => "Image Description",
+                'Type' => "String" ),
+
+271 => array(   'Name' => "Make (Manufacturer)",
+                'Type' => "String" ),
+
+272 => array(   'Name' => "Model",
+                'Type' => "String" ),
+
+305 => array(   'Name' => "Software or Firmware",
+                'Type' => "String" ),
+
+315 => array(   'Name' => "Artist Name",
+                'Type' => "String" ),
+
+700 => array(   'Name' => "Embedded XMP Block",        // Change: Added embedded XMP as of version 1.11
+                'Type' => "XMP" ),
+
+33432 => array( 'Name' => "Copyright Information",
+                'Type' => "String" ),
+
+34665 => array( 'Name' => "EXIF Image File Directory (IFD)",
+                'Type' => "SubIFD",
+                'Tags Name' => "EXIF" ),
+
+33723 => array( 'Name' => "IPTC Records",
+                'Type' => "IPTC" ),
+
+34377 => array( 'Name' => "Embedded Photoshop IRB",    // Change: Added embedded IRB as of version 1.11
+                'Type' => "IRB" ),
+
+34853 => array( 'Name' => "GPS Info Image File Directory (IFD)",        // Change: Moved GPS IFD tag to correct location as of version 1.11
+                'Type' => "SubIFD",
+                'Tags Name' => "GPS" ),
+
+50341 => array( 'Name' => "Print Image Matching Info",
+                'Type' => "PIM" ),
+
+),
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* EXIF Tags                                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+
+'EXIF' => array (
+
+// Exif IFD
+36864 => array( 'Name' => "Exif Version",
+                'Type' => "String" ),
+
+40965 => array( 'Name' => "Interoperability Image File Directory (IFD)",
+                'Type' => "SubIFD",
+                'Tags Name' => "Interoperability" ),
+
+// Change: removed GPS IFD tag from here as it was incorrect location - as of version 1.11
+
+40960 => array( 'Name' => "FlashPix Version",
+                'Type' => "String" ),
+
+40961 => array( 'Name' => "Colour Space",
+                'Type' => "Lookup",
+                1 => "sRGB",
+                0xFFFF => "Uncalibrated" ),
+
+40962 => array( 'Name' => "Pixel X Dimension",
+                'Type' => "Numeric",
+                'Units'=> "pixels" ),
+
+40963 => array( 'Name' => "Pixel Y Dimension",
+                'Type' => "Numeric",
+                'Units' => "pixels" ),
+
+37121 => array( 'Name' => "Components Configuration",
+                'Type' => "Special" ),
+
+37122 => array( 'Name' => "Compressed Bits Per Pixel",
+                'Type' => "Numeric",
+                'Units' => "bits" ),
+
+37500 => array( 'Name' => "Maker Note",
+                'Type' => "Maker Note" ),
+
+37510 => array( 'Name' => "User Comment",
+                'Type' => "Character Coded String" ),
+
+40964 => array( 'Name' => "Related Sound File",
+                'Type' => "String" ),
+
+36867 => array( 'Name' => "Date and Time of Original",
+                'Type' => "String",
+                'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ),
+
+36868 => array( 'Name' => "Date and Time when Digitized",
+                'Type' => "String",
+                'Units' => " (Format: YYYY:MM:DD HH:mm:SS)" ),
+
+37520 => array( 'Name' => "Sub Second Time",
+                'Type' => "String" ),
+
+37521 => array( 'Name' => "Sub Second Time of Original",
+                'Type' => "String" ),
+
+37522 => array( 'Name' => "Sub Second Time when Digitized",
+                'Type' => "String" ),
+
+33434 => array( 'Name' => "Exposure Time",
+                'Type' => "Numeric",
+                'Units' => "seconds" ),
+
+37377 => array( 'Name' => "APEX Shutter Speed Value (Tv)",
+                'Type' => "Numeric" ),
+
+37378 => array( 'Name' => "APEX Aperture Value (Av)",
+                'Type' => "Numeric" ),
+
+37379 => array( 'Name' => "APEX Brightness Value (Bv)",
+                'Type' => "Numeric" ),
+
+37380 => array( 'Name' => "APEX Exposure Bias Value (Exposure Compensation)",
+                'Type' => "Numeric",
+                'Units' => "EV" ),
+
+42240 => array( 'Name' => "Gamma Compensation for Playback",
+                'Type' => "Numeric" ),
+
+
+37381 => array( 'Name' => "APEX Maximum Aperture Value",
+                'Type' => "Numeric" ),
+
+37382 => array( 'Name' => "Subject Distance",
+                'Type' => "Numeric",
+                'Units' => "metres" ),
+
+37383 => array( 'Name' => "Metering Mode",
+                'Type' => "Lookup",
+                0 => "Unknown",
+                1 => "Average",
+                2 => "Center Weighted Average",
+                3 => "Spot",
+                4 => "Multi Spot",
+                5 => "Pattern",
+                6 => "Partial",
+                255 => "Other" ),
+
+37384 => array( 'Name' => "Light Source",
+                'Type' => "Lookup",
+                0 => "Unknown",
+                1 => "Daylight",
+                2 => "Fluorescent",
+                3 => "Tungsten (incandescent light)",
+                4 => "Flash",
+                9 => "Fine weather",
+                10 => "Cloudy weather",
+                11 => "Shade",
+                12 => "Daylight fluorescent (D 5700 – 7100K)",
+                13 => "Day white fluorescent (N 4600 – 5400K)",
+                14 => "Cool white fluorescent (W 3900 – 4500K)",
+                15 => "White fluorescent (WW 3200 – 3700K)",
+                17 => "Standard light A",
+                18 => "Standard light B",
+                19 => "Standard light C",
+                20 => "D55",
+                21 => "D65",
+                22 => "D75",
+                23 => "D50",
+                24 => "ISO studio tungsten",
+                255 => "Other" ),
+
+37385 => array( 'Name' => "Flash",
+                'Type' => "Lookup",
+                0  => "Flash did not fire",
+                1  => "Flash fired",
+                5  => "Strobe return light not detected",
+                7  => "Strobe return light detected",
+                9  => "Flash fired, compulsory flash mode",
+                13 => "Flash fired, compulsory flash mode, return light not detected",
+                15 => "Flash fired, compulsory flash mode, return light detected",
+                16 => "Flash did not fire, compulsory flash suppression mode",
+                24 => "Flash did not fire, auto mode",
+                25 => "Flash fired, auto mode",
+                29 => "Flash fired, auto mode, return light not detected",
+                31 => "Flash fired, auto mode, return light detected",
+                32 => "No flash function",
+                65 => "Flash fired, red-eye reduction mode",
+                69 => "Flash fired, red-eye reduction mode, return light not detected",
+                71 => "Flash fired, red-eye reduction mode, return light detected",
+                73 => "Flash fired, compulsory flash mode, red-eye reduction mode",
+                77 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
+                79 => "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
+                89 => "Flash fired, auto mode, red-eye reduction mode",
+                93 => "Flash fired, auto mode, return light not detected, red-eye reduction mode",
+                95 => "Flash fired, auto mode, return light detected, red-eye reduction mode" ),
+
+37386 => array( 'Name' => "FocalLength",
+                'Type' => "Numeric",
+                'Units' => "mm" ),
+
+37396 => array( 'Name' => "Subject Area",
+                'Type' => "Numeric",
+                'Units' => "( Two Values: x,y coordinates,  Three Values: x,y coordinates, diameter,  Four Values: center x,y coordinates, width, height)" ),
+
+33437 => array( 'Name' => "Aperture F Number",
+                'Type' => "Numeric" ),
+
+34850 => array( 'Name' => "Exposure Program",
+                'Type' => "Lookup",
+                0 => "Not defined",
+                1 => "Manual",
+                2 => "Normal program",
+                3 => "Aperture priority",
+                4 => "Shutter priority",
+                5 => "Creative program (biased toward depth of field)",
+                6 => "Action program (biased toward fast shutter speed)",
+                7 => "Portrait mode (for closeup photos with the background out of focus)",
+                8 => "Landscape mode (for landscape photos with the background in focus)" ),
+
+34852 => array( 'Name' => "Spectral Sensitivity",
+                'Type' => "String" ),
+
+34855 => array( 'Name' => "ISO Speed Ratings",
+                'Type' => "Numeric" ),
+
+34856 => array( 'Name' => "Opto-Electronic Conversion Function",
+                'Type' => "Unknown" ),
+
+41483 => array( 'Name' => "Flash Energy",
+                'Type' => "Numeric",
+                'Units' => "Beam Candle Power Seconds (BCPS)" ),
+
+41484 => array( 'Name' => "Spatial Frequency Response",
+                'Type' => "Unknown" ),
+
+41486 => array( 'Name' => "Focal Plane X Resolution",
+                'Type' => "Numeric",
+                'Units' => "pixels per 'Focal Plane Resolution Unit'" ),
+
+41487 => array( 'Name' => "Focal Plane Y Resolution",
+                'Type' => "Numeric",
+                'Units' => "pixels per 'Focal Plane Resolution Unit'" ),
+
+41488 => array( 'Name' => "Focal Plane Resolution Unit",
+                'Type' => "Lookup",
+                2 => "Inches",
+                3 => "Centimetres" ),
+
+41492 => array( 'Name' => "Subject Location",
+                'Type' => "Numeric",
+                'Units' => "(x,y pixel coordinates of subject)" ),
+
+41493 => array( 'Name' => "Exposure Index",
+                'Type' => "Numeric" ),
+
+41495 => array( 'Name' => "Sensing Method",
+                'Type' => "Lookup",
+                1 => "Not defined",
+                2 => "One-chip colour area sensor",
+                3 => "Two-chip colour area sensor",
+                4 => "Three-chip colour area sensor",
+                5 => "Colour sequential area sensor",
+                7 => "Trilinear sensor",
+                8 => "Colour sequential linear sensor" ),
+
+41728 => array( 'Name' => "File Source",
+                'Type' => "Lookup",
+                3 => "Digital Still Camera" ),
+
+41729 => array( 'Name' => "Scene Type",
+                'Type' => "Lookup",
+                1 => "A directly photographed image" ),
+
+41730 => array( 'Name' => "Colour Filter Array Pattern",
+                'Type' => "Special" ),
+
+41985 => array( 'Name' => "Special Processing (Custom Rendered)",
+                'Type' => "Lookup",
+                0 => "Normal process",
+                1 => "Custom process" ),
+
+41986 => array( 'Name' => "Exposure Mode",
+                'Type' => "Lookup",
+                0 => "Auto exposure",
+                1 => "Manual exposure",
+                2 => "Auto bracket" ),
+
+41987 => array( 'Name' => "White Balance",
+                'Type' => "Lookup",
+                0 => "Auto white balance",
+                1 => "Manual white balance" ),
+
+41988 => array( 'Name' => "Digital Zoom Ratio",
+                'Type' => "Numeric",
+                'Units' => " ( Zero = Digital Zoom Not Used )" ),
+
+41989 => array( 'Name' => "Equivalent Focal Length In 35mm Film",
+                'Type' => "Numeric",
+                'Units' => "mm" ),
+
+41990 => array( 'Name' => "Scene Capture Type",
+                'Type' => "Lookup",
+                0 => "Standard",
+                1 => "Landscape",
+                2 => "Portrait",
+                3 => "Night scene" ),
+
+41991 => array( 'Name' => "Gain Control",
+                'Type' => "Lookup",
+                0 => "None",
+                1 => "Low gain up",
+                2 => "High gain up",
+                3 => "Low gain down",
+                4 => "High gain down" ),
+
+41992 => array( 'Name' => "Contrast",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Soft",
+                2 => "Hard" ),
+
+41993 => array( 'Name' => "Saturation",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Low saturation",
+                2 => "High saturation" ),
+
+41994 => array( 'Name' => "Sharpness",
+                'Type' => "Lookup",
+                0 => "Normal",
+                1 => "Soft",
+                2 => "Hard" ),
+
+41995 => array( 'Name' => "Device Setting Description",
+                'Type' => "Unknown" ),
+
+41996 => array( 'Name' => "Subject Distance Range",
+                'Type' => "Lookup",
+                0 => "Unknown",
+                1 => "Macro",
+                2 => "Close view",
+                3 => "Distant view" ),
+
+42016 => array( 'Name' => "Image Unique ID",
+                'Type' => "String" ),
+
+
+
+//  11  => "ACDComment",
+//  255 => "NewSubfileType"
+
+
+),
+
+
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* Interoperability Tags                                                     */
+/*                                                                           */
+/*****************************************************************************/
+
+"Interoperability" => array(
+
+1 => array(     'Name' => "Interoperability Index",
+                'Type' => "String" ),
+
+2 => array(     'Name' => "Interoperability Version",
+                'Type' => "String" ),
+
+4096 => array(  'Name' => "Related Image File Format",
+                'Type' => "String" ),
+
+4097 => array(  'Name' => "Related Image File Width",
+                'Type' => "Numeric",
+                'Units' => "pixels" ),
+
+4098 => array(  'Name' => "Related Image File Length",
+                'Type' => "Numeric",
+                'Units' => "pixels " )
+
+),
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* GPS Tags                                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+"GPS" => array(
+
+0 => array(     'Name' => "GPS Tag Version",
+                'Type' => "Numeric",
+                'Units' => "(e.g.: 2.2.0.0 = Version 2.2 )" ),
+
+1 => array(     'Name' => "North or South Latitude",
+                'Type' => "String" ),
+
+2 => array(     'Name' => "Latitude",
+                'Type' => "Numeric",
+                'Units' => "(Degrees Minutes Seconds North or South)" ),
+
+3 => array(     'Name' => "East or West Longitude",
+                'Type' => "String" ),
+
+4 => array(     'Name' => "Longitude",
+                'Type' => "Numeric",
+                'Units' => "(Degrees Minutes Seconds East or West)" ),
+
+5 => array(     'Name' => "Altitude Reference",
+                'Type' => "Lookup",
+                0 => "Sea Level",
+                1 => "Sea level reference (negative value)" ),
+
+6 => array(     'Name' => "Altitude",
+                'Type' => "Numeric",
+                'Units' => "Metres with respect to Altitude Reference" ),
+
+7 => array(     'Name' => "GPS Time (atomic clock)",
+                'Type' => "Numeric",
+                'Units' => "(Hours Minutes Seconds)" ),
+
+8 => array(     'Name' => "GPS Satellites used for Measurement",
+                'Type' => "String" ),
+
+9 => array(     'Name' => "GPS Receiver Status",
+                'Type' => "Lookup",
+                'A' => "Measurement in progress",          // Change: Fixed tag values as of version 1.11
+                'V' => "Measurement Interoperability" ),
+
+10 => array(    'Name' => "GPS Measurement Mode",
+                'Type' => "Lookup",
+                2 => "2-dimensional measurement",         // Change: Fixed tag values as of version 1.11
+                3 => "3-dimensional measurement" ),
+
+11 => array(    'Name' => "Measurement Precision",
+                'Type' => "Numeric",
+                'Units' => "(Data Degree of Precision, Horizontal for 2D, Position for 3D)" ),
+
+12 => array(    'Name' => "Speed Unit",
+                'Type' => "Lookup",
+                'K' => "Kilometers per Hour",            // Change: Fixed tag values as of version 1.11
+                'M' => "Miles per Hour",
+                'N' => "Knots" ),
+
+13 => array(    'Name' => "Speed of GPS receiver",
+                'Type' => "Numeric",
+                'Units' => "Speed Units" ),
+
+14 => array(    'Name' => "Reference for direction of Movement",
+                'Type' => "Lookup",                     // Change: Fixed tag values as of version 1.11
+                'T' => "True North",
+                'M' => "Magnetic North" ),
+
+15 => array(    'Name' => "Direction of Movement",
+                'Type' => "Numeric",
+                'Units' => "Degrees relative to Movement Direction Reference" ),
+
+16 => array(    'Name' => "Reference for Direction of Image",
+                'Type' => "Lookup",
+                'T' => "True North",                    // Change: Fixed tag values as of version 1.11
+                'M' => "Magnetic North" ),
+
+17 => array(    'Name' => "Direction of Image",
+                'Type' => "Numeric",
+                'Units' => "Degrees relative to Image Direction Reference" ),
+
+18 => array(    'Name' => "Geodetic Survey Datum Used",
+                'Type' => "String" ),
+
+19 => array(    'Name' => "Destination - North or South Latitude",
+                'Type' => "String" ),
+
+20 => array(    'Name' => "Latitude of Destination",
+                'Type' => "Numeric",
+                'Units' => "(Degrees Minutes Seconds North or South)" ),
+
+21 => array(    'Name' => "Destination - East or West Longitude",
+                'Type' => "String" ),
+
+22 => array(    'Name' => "Longitude of Destination",
+                'Type' => "Numeric",
+                'Units' => "(Degrees Minutes Seconds East or West)" ),
+
+23 => array(    'Name' => "Reference for Bearing of Destination",
+                'Type' => "Lookup",
+                'T' => "True North",                    // Change: Fixed tag values as of version 1.11
+                'M' => "Magnetic North" ),
+
+24 => array(    'Name' => "Bearing of Destination",
+                'Type' => "Numeric",
+                'Units' => "Degrees relative to Destination Bearing Reference" ),
+
+25 => array(    'Name' => "Units for Distance to Destination",
+                'Type' => "Lookup",
+                'K' => "Kilometres",                    // Change: Fixed tag values as of version 1.11
+                'M' => "Miles",
+                'N' => "Nautical Miles" ),
+
+26 => array(    'Name' => "Distance to Destination",
+                'Type' => "Numeric",
+                'Units' => "Destination Distance Units" ),
+
+27 => array(    'Name' => "Name of GPS Processing Method",
+                'Type' => "Character Coded String" ),
+
+28 => array(    'Name' => "Name of GPS Area",
+                'Type' => "Character Coded String" ),
+
+29 => array(    'Name' => "GPS Date",
+                'Type' => "Numeric",
+                'Units'=> " (Format: YYYY:MM:DD HH:mm:SS)" ),
+
+30 => array(    'Name' => "GPS Differential Correction",
+                'Type' => "Lookup",
+                0 => "Measurement without differential correction",
+                1 => "Differential correction applied" ),
+
+),
+
+
+
+
+
+
+
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* META (App3) Tags                                                          */
+/*                                                                           */
+/*****************************************************************************/
+
+"Meta" => array(
+
+
+50000 => array( 'Name' => "CaptureDevice.FilmProductCode",
+                'Type' => "Unknown" ),
+
+50001 => array( 'Name' => "DigitalProcess.ImageSourceEK",
+                'Type' => "Unknown" ),
+
+50002 => array( 'Name' => "CaptureConditions.PAR",
+                'Type' => "Unknown" ),
+
+50003 => array( 'Name' => "CaptureDevice.CameraOwner.EK",
+                'Type' => "Character Coded String" ),
+
+50004 => array( 'Name' => "CaptureDevice.SerialNumber.Camera",
+                'Type' => "Unknown" ),
+
+50005 => array( 'Name' => "SceneContent.GroupCaption.UserSelectGroupTitle",
+                'Type' => "Unknown" ),
+
+50006 => array( 'Name' => "OutputOrder.Information.DealerIDNumber",
+                'Type' => "Unknown" ),
+
+50007 => array( 'Name' => "CaptureDevice.FID",
+                'Type' => "Unknown" ),
+
+50008 => array( 'Name' => "OutputOrder.Information.EnvelopeNumber",
+                'Type' => "Unknown" ),
+
+50009 => array( 'Name' => "OutputOrder.SimpleRenderInst.FrameNumber",
+                'Type' => "Unknown" ),
+
+50010 => array( 'Name' => "CaptureDevice.FilmCategory",
+                'Type' => "Unknown" ),
+
+50011 => array( 'Name' => "CaptureDevice.FilmGencode",
+                'Type' => "Unknown" ),
+
+50012 => array( 'Name' => "CaptureDevice.Scanner.ModelAndVersion",
+                'Type' => "Unknown" ),
+
+50013 => array( 'Name' => "CaptureDevice.FilmSize",
+                'Type' => "Unknown" ),
+
+50014 => array( 'Name' => "DigitalProcess.History.SBARGBShifts",
+                'Type' => "Unknown" ),
+
+50015 => array( 'Name' => "DigitalProcess.History.SBAInputImageColourspace",
+                'Type' => "Unknown" ),
+
+50016 => array( 'Name' => "DigitalProcess.History.SBAInputImageBitDepth",
+                'Type' => "Unknown" ),
+
+50017 => array( 'Name' => "DigitalProcess.History.SBAExposureRecord",
+                'Type' => "Unknown" ),
+
+50018 => array( 'Name' => "DigitalProcess.History.UserAdjSBARGBShifts",
+                'Type' => "Unknown" ),
+
+50019 => array( 'Name' => "DigitalProcess.ImageRotationStatus",
+                'Type' => "Unknown" ),
+
+50020 => array( 'Name' => "DigitalProcess.RollGuid.Elements",
+                'Type' => "Unknown" ),
+
+50021 => array( 'Name' => "ImageContainer.MetadataNumber",
+                'Type' => "String" ),
+
+50022 => array( 'Name' => "DigitalProcess.History.EditTagArray",
+                'Type' => "Unknown" ),
+
+50023 => array( 'Name' => "CaptureConditions.Magnification",
+                'Type' => "Unknown" ),
+
+50028 => array( 'Name' => "CaptureDevice.NativePhysicalXResolution",
+                'Type' => "Unknown" ),
+
+50029 => array( 'Name' => "CaptureDevice.NativePhysicalYResolution",
+                'Type' => "Unknown" ),
+
+50030 => array( 'Name' => "Kodak Special Effects IFD",
+                'Type' => "SubIFD",
+                'Tags Name' => "KodakSpecialEffects" ),
+
+50031 => array( 'Name' => "Kodak Borders IFD",
+                'Type' => "SubIFD",
+                'Tags Name' => "KodakBorders" ),
+
+50042 => array( 'Name' => "CaptureDevice.NativePhysicalResolutionUnit",
+                'Type' => "Unknown" ),
+
+50200 => array( 'Name' => "ImageContainer.SourceImageDirectory",
+                'Type' => "Unknown" ),
+
+50201 => array( 'Name' => "ImageContainer.SourceImageFileName",
+                'Type' => "Unknown" ),
+
+50202 => array( 'Name' => "ImageContainer.SourceImageVolumeName",
+                'Type' => "Unknown" ),
+
+50284 => array( 'Name' => "CaptureConditions.PrintQuantity",
+                'Type' => "Unknown" ),
+
+50286 => array( 'Name' => "DigitalProcess.ImagePrintStatus",
+                'Type' => "Unknown" )
+
+),
+
+
+
+/*****************************************************************************/
+/*                                                                           */
+/* Kodak Special Effects IFD Tags                                            */
+/*                                                                           */
+/*****************************************************************************/
+
+"KodakSpecialEffects" => array(
+
+0 => array(     'Name' => "Digital Effects Version",
+                'Type' => "Numeric" ),
+
+1 => array(     'Name' => "Digital Effects Name",
+                'Type' => "Character Coded String" ),
+
+2 => array(     'Name' => "Digital Effects Type",
+                'Type' => "Lookup",
+                0 => "None Applied" )
+
+),
+
+/*****************************************************************************/
+/*                                                                           */
+/* Kodak Borders IFD Tags                                                    */
+/*                                                                           */
+/*****************************************************************************/
+
+"KodakBorders" => array(
+
+0 => array(     'Name' => "Borders Version",
+                'Type' => "Numeric" ),
+
+1 => array(     'Name' => "Border Name",
+                'Type' => "Character Coded String" ),
+
+2 => array(     'Name' => "Border ID",
+                'Type' => "Numeric" ),
+
+3 => array(     'Name' => "Border Location",
+                'Type' => "Lookup" ),
+
+4 => array(     'Name' => "Border Type",
+                'Type' => "Lookup",
+                0 => "None" ),
+
+8 => array(     'Name' => "Watermark Type",
+                'Type' => "Lookup",
+                0 => "None" )
+
+),
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IFD_Tag_Definitions
+******************************************************************************/
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Example.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Example.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Example.php	2011/08/17 20:08:58	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Example.php	2005/01/20 22:57:14
@@ -0,0 +1,218 @@
+<html>
+
+<!--***************************************************************************
+*
+* Filename:     Example.php
+*
+* Description:  An example of how the PHP JPEG Metadata Toolkit can be used to
+*               display JPEG Metadata.
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.10 : Changed name of GET parameter from 'filename' to 'jpeg_fname'
+*                              to stop script-kiddies using the google command 'allinurl:*.php?filename=*'
+*                              to find servers to attack
+*                              Changed behavior when no filename is given, to be cleaner
+*               1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*                              Changed this example file to be easily relocatable
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+***************************************************************************-->
+
+        <head>
+
+                <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+                <STYLE TYPE="text/css" MEDIA="screen, print, projection">
+                <!--
+
+                        BODY { background-color:#505050; color:#F0F0F0 }
+                        a  { color:orange  }
+                        .EXIF_Main_Heading { color:red }
+                        .EXIF_Secondary_Heading{ color: orange}
+                        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+                        .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;}
+
+                -->
+                </STYLE>
+
+
+                <?php
+                        // Turn off Error Reporting
+                        error_reporting ( 0 );
+
+                        // Change: Allow this example file to be easily relocatable - as of version 1.11
+                        $Toolkit_Dir = "./";     // Ensure dir name includes trailing slash
+
+                        // Hide any unknown EXIF tags
+                        $GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE;
+
+                        include $Toolkit_Dir . 'Toolkit_Version.php';          // Change: added as of version 1.11
+                        include $Toolkit_Dir . 'JPEG.php';                     // Change: Allow this example file to be easily relocatable - as of version 1.11
+                        include $Toolkit_Dir . 'JFIF.php';
+                        include $Toolkit_Dir . 'PictureInfo.php';
+                        include $Toolkit_Dir . 'XMP.php';
+                        include $Toolkit_Dir . 'Photoshop_IRB.php';
+                        include $Toolkit_Dir . 'EXIF.php';
+
+                        // Retrieve the JPEG image filename from the http url request
+                        if ( ( !array_key_exists( 'jpeg_fname', $GLOBALS['HTTP_GET_VARS'] ) ) ||
+                             ( $GLOBALS['HTTP_GET_VARS']['jpeg_fname'] == "" ) )
+                        {
+                                echo "<title>No image filename defined</title>\n";
+                                echo "</head>\n";
+                                echo "<body>\n";
+                                echo "<p>No image filename defined - use GET method with field: jpeg_fname</p>\n";
+                                echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n";         // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11
+                                echo "</body>\n";
+                                exit( );
+                        }
+                        else
+                        {
+                                $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname'];
+                        }
+
+
+                        // Output the title
+                        echo "<title>Metadata details for $filename</title>";
+
+                        // Retrieve the header information
+                        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+                 ?>
+
+        </head>
+
+        <body>
+
+                <p>Interpreted using: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>    <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+                <br>
+                <br>
+
+                <h1><B><U>Metadata for &quot;<?php echo $filename; ?>&quot;</U></B></h1>
+                <br>
+
+                <!-- Output a link allowing user to edit the Photoshop File Info
+                     Change: Allow this example file to be easily relocatable - as of version 1.11
+                -->
+                <?php  $relative_filename = get_relative_path( $filename, $Toolkit_Dir );  ?>
+                <h4><a href="<?php echo $Toolkit_Dir."Edit_File_Info_Example.php?jpeg_fname=$relative_filename"; ?>" >Click here to edit the Photoshop File Info for this file</a></h4>
+                <br>
+
+
+
+                <!-- Output the information about the APP segments -->
+                <?php   echo Generate_JPEG_APP_Segment_HTML( $jpeg_header_data ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the Intrinsic JPEG Information -->
+                <?php   echo Interpret_intrinsic_values_to_HTML( get_jpeg_intrinsic_values( $jpeg_header_data ) );  ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the JPEG Comment -->
+                <?php   echo Interpret_Comment_to_HTML( $jpeg_header_data ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the JPEG File Interchange Format Information -->
+                <?php   echo Interpret_JFIF_to_HTML( get_JFIF( $jpeg_header_data ), $filename ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the JFIF Extension Information -->
+                <?php   echo Interpret_JFXX_to_HTML( get_JFXX( $jpeg_header_data ), $filename ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the Picture Info Text -->
+                <?php   echo Interpret_App12_Pic_Info_to_HTML( $jpeg_header_data ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the EXIF Information -->
+                <?php   echo Interpret_EXIF_to_HTML( get_EXIF_JPEG( $filename ), $filename );  ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the XMP Information -->
+                <?php   echo Interpret_XMP_to_HTML( read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) ) ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the Photoshop IRB (including the IPTC-NAA info -->
+                <?php   echo Interpret_IRB_to_HTML( get_Photoshop_IRB( $jpeg_header_data ), $filename ); ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Output the Meta Information -->
+                <?php   echo Interpret_EXIF_to_HTML( get_Meta_JPEG( $filename ), $filename );  ?>
+
+                <BR>
+                <HR>
+                <BR>
+
+                <!-- Display the original image -->
+
+                <h2>Original Image</h2>
+                <?php   echo "<img src=\"$filename\">";  ?>
+
+
+                <BR>
+                <BR>
+                <BR>
+                <p>Interpreted using:</p>
+                <p><a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>     <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+
+        </body>
+
+</html>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Write_File_Info.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Write_File_Info.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Write_File_Info.php	2011/08/17 20:09:02	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Write_File_Info.php	2005/01/20 10:31:32
@@ -0,0 +1,196 @@
+<html>
+
+<!--***************************************************************************
+*
+* Filename:     Write_File_Info.php
+*
+* Description:  An example file showing how a user can write the metadata of an
+*               image over the internet in the same way that Photoshop
+*               edits 'File Info' data.
+*               This script pairs with Edit_File_Info_Example.php, receiving
+*               and processing the data from the HTML form in that script
+*
+* Author:       Evan Hunter
+*
+* Date:         17/11/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*                              Changed error reporting to no errors
+*                              Removed limitation on file being in current directory
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+***************************************************************************-->
+
+
+        <head>
+                <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+                <STYLE TYPE="text/css" MEDIA="screen, print, projection">
+                <!--
+
+                        BODY { background-color:#505050; color:#F0F0F0 }
+                        a  { color:orange  }
+                        .EXIF_Main_Heading { color:red }
+                        .EXIF_Secondary_Heading{ color: orange}
+                        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+                        .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;}
+
+                -->
+                </STYLE>
+
+                <title>Writing Photoshop File Info Metadata</title>
+        </head>
+
+        <body>
+                <?php include 'Toolkit_Version.php'; ?>
+                <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>                   <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+                <br>
+                <br>
+
+                <?php
+                        // Turn off Error Reporting
+                        error_reporting ( 0 );          // Change: changed to no reporting -  as of version 1.11
+
+                        include 'Toolkit_Version.php';  // Change: added as of version 1.11
+
+                        // Include the required files for reading and writing Photoshop File Info
+                        include 'JPEG.php';
+                        include 'XMP.php';
+                        include 'Photoshop_IRB.php';
+                        include 'EXIF.php';
+                        include 'Photoshop_File_Info.php';
+
+
+                        // Copy all of the HTML Posted variables into an array
+                        $new_ps_file_info_array = $GLOBALS['HTTP_POST_VARS'];
+
+                        // Some characters are escaped with backslashes in HTML Posted variable
+                        // Cycle through each of the HTML Posted variables, and strip out the slashes
+                        foreach( $new_ps_file_info_array as $var_key => $var_val )
+                        {
+                                $new_ps_file_info_array[ $var_key ] = stripslashes( $var_val );
+                        }
+
+                        // Keywords should be an array - explode it on newline boundarys
+                        $new_ps_file_info_array[ 'keywords' ] = explode( "\n", trim( $new_ps_file_info_array[ 'keywords' ] ) );
+
+                        // Supplemental Categories should be an array - explode it on newline boundarys
+                        $new_ps_file_info_array[ 'supplementalcategories' ] = explode( "\n", trim( $new_ps_file_info_array[ 'supplementalcategories' ] ) );
+
+                        // Make the filename easier to access
+                        $filename = $new_ps_file_info_array[ 'filename' ];
+
+                        // Protect against hackers editing other files
+                        $path_parts = pathinfo( $filename );
+                        if ( strcasecmp( $path_parts["extension"], "jpg" ) != 0 )
+                        {
+                                echo "Incorrect File Type - JPEG Only\n";
+                                exit( );
+                        }
+                        // Change: removed limitation on file being in current directory - as of version 1.11
+
+                        // Retrieve the header information
+                        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+                        // Retreive the EXIF, XMP and Photoshop IRB information from
+                        // the existing file, so that it can be updated
+                        $Exif_array = get_EXIF_JPEG( $filename );
+                        $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
+                        $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
+
+                        // Update the JPEG header information with the new Photoshop File Info
+                        $jpeg_header_data = put_photoshop_file_info( $jpeg_header_data, $new_ps_file_info_array, $Exif_array, $XMP_array, $IRB_array );
+
+                        // Check if the Update worked
+                        if ( $jpeg_header_data == FALSE )
+                        {
+                                // Update of file info didn't work - output error message
+                                echo "Error - Failure update Photoshop File Info : $filename <br>\n";
+
+                                // Output HTML with the form and data which was
+                                // sent, to allow the user to fix it
+
+                                $outputfilename = $filename;
+                                include "Edit_File_info.php";
+                                echo "</body>\n";
+                                echo "</html>\n";
+
+                                // Abort processing
+                                exit( );
+                        }
+
+                        // Attempt to write the new JPEG file
+                        if ( FALSE == put_jpeg_header_data( $filename, $filename, $jpeg_header_data ) )
+                        {
+                                // Writing of the new file didn't work - output error message
+                                echo "Error - Failure to write new JPEG : $filename <br>\n";
+
+                                // Output HTML with the form and data which was
+                                // sent, to allow the user to fix it
+
+                                $outputfilename = $filename;
+                                include "Edit_File_info.php";
+                                echo "</body>\n";
+                                echo "</html>\n";
+
+                                // Abort processing
+                                exit( );
+                        }
+
+
+                        // Writing of new JPEG succeeded
+
+                        // Output information about new file
+
+                        echo "<h1>DONE! - $filename updated</h1>\n";
+                        echo "<p><a href=\"Example.php?jpeg_fname=$filename\" >View Full Metatdata Information</a></p>\n";
+                        echo "<p><a href=\"Edit_File_Info_Example.php?jpeg_fname=$filename\" >Re-Edit Photoshop File Info</a></p>\n";
+                        echo "<br><br>\n";
+                        echo "<p>Below is the updated image, you can save it and look at the changed metadata in your favorite image editor</p>\n";
+                        echo "<p><img src=\"$filename\" ></p>\n";
+
+
+                ?>
+
+                <br>
+                <br>
+                <br>
+                <br>
+
+
+                <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>  <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+
+                <br>
+                <br>
+
+        </body>
+
+</html>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Unicode.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Unicode.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Unicode.php	2011/08/17 20:09:02	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Unicode.php	2004/11/22 21:26:56
@@ -0,0 +1,1227 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     Unicode.php
+*
+* Description:  Provides functions for handling Unicode strings in PHP without
+*               needing to configure the non-default mbstring extension
+*
+* Author:       Evan Hunter
+*
+* Date:         27/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.10
+*
+* Changes:      1.00 -> 1.10 : Added the following functions:
+*                              smart_HTML_Entities
+*                              smart_htmlspecialchars
+*                              HTML_UTF16_UnEscape
+*                              HTML_UTF8_UnEscape
+*                              changed HTML_UTF8_Escape and HTML_UTF16_Escape to
+*                              use smart_htmlspecialchars, so that characters which
+*                              were already escaped would remain intact
+*
+*
+* URL:          http://electronics.ozhiker.com
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// TODO: UTF-16 functions have not been tested fully
+
+
+
+/******************************************************************************
+*
+* Unicode UTF-8 Encoding Functions
+*
+* Description:  UTF-8 is a Unicode encoding system in which extended characters
+*               use only the upper half (128 values) of the byte range, thus it
+*               allows the use of normal 7-bit ASCII text.
+*               7-Bit ASCII will pass straight through UTF-8 encoding/decoding without change
+*
+*
+* The encoding is as follows:
+* Unicode Value          :  Binary representation (x=data bit)
+*--------------------------------------------------------------------------------
+* U-00000000 - U-0000007F:  0xxxxxxx                      <- This is 7-bit ASCII
+* U-00000080 - U-000007FF:  110xxxxx 10xxxxxx
+* U-00000800 - U-0000FFFF:  1110xxxx 10xxxxxx 10xxxxxx
+* U-00010000 - U-001FFFFF:  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+* U-00200000 - U-03FFFFFF:  111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+* U-04000000 - U-7FFFFFFF:  1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+*--------------------------------------------------------------------------------
+*
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Unicode UTF-16 Encoding Functions
+*
+* Description:  UTF-16 is a Unicode encoding system uses 16 bit values for representing
+*               characters.
+*               It also has an extended set of characters available by the use
+*               of surrogate pairs, which are a pair of 16 bit values, giving a
+*               total data length of 20 useful bits.
+*
+*
+* The encoding is as follows:
+* Unicode Value          :  Binary representation (x=data bit)
+*--------------------------------------------------------------------------------
+* U-000000 - U-00D7FF:  xxxxxxxx xxxxxxxx
+* U-00D800 - U-00DBFF:  Not available - used for high surrogate pairs
+* U-00DC00 - U-00DFFF:  Not available - used for low surrogate pairs
+  U-00E000 - U-00FFFF:  xxxxxxxx xxxxxxxx
+* U-010000 - U-10FFFF:  110110ww wwxxxxxx  110111xx xxxxxxxx      ( wwww = (uni-0x10000)/0x10000 )
+*--------------------------------------------------------------------------------
+*
+*  Surrogate pair Calculations
+*
+*  $hi = ($uni - 0x10000) / 0x400 + 0xD800;
+*  $lo = ($uni - 0x10000) % 0x400 + 0xDC00;
+*
+*
+*  $uni = 0x10000 + ($hi - 0xD800) * 0x400 + ($lo - 0xDC00);
+*
+*
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     UTF8_fix
+*
+* Description:  Checks a string for badly formed Unicode UTF-8 coding and
+*               returns the same string containing only the parts which
+*               were properly formed UTF-8 data.
+*
+* Parameters:   utf8_text - a string with possibly badly formed UTF-8 data
+*
+* Returns:      output - the well formed UTF-8 version of the string
+*
+******************************************************************************/
+
+function UTF8_fix( $utf8_text )
+{
+        // Initialise the current position in the string
+        $pos = 0;
+
+        // Create a string to accept the well formed output
+        $output = "" ;
+
+        // Cycle through each group of bytes, ensuring the coding is correct
+        while ( $pos < strlen( $utf8_text ) )
+        {
+                // Retreive the current numerical character value
+                $chval = ord($utf8_text{$pos});
+
+                // Check what the first character is - it will tell us how many bytes the
+                // Unicode value covers
+
+                if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) )
+                {
+                        // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character
+                        $bytes = 1;
+                }
+                else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) )
+                {
+                        // 2 Byte UTF-8 Unicode Character
+                        $bytes = 2;
+                }
+                else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) )
+                {
+                        // 3 Byte UTF-8 Unicode Character
+                        $bytes = 3;
+                }
+                else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) )
+                {
+                        // 4 Byte UTF-8 Unicode Character
+                        $bytes = 4;
+                }
+                else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) )
+                {
+                        // 5 Byte UTF-8 Unicode Character
+                        $bytes = 5;
+                }
+                else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) )
+                {
+                        // 6 Byte UTF-8 Unicode Character
+                        $bytes = 6;
+                }
+                else
+                {
+                        // Invalid Code - skip character and do nothing
+                        $bytes = 0;
+                        $pos++;
+                }
+
+
+                // check that there is enough data remaining to read
+                if (($pos + $bytes - 1) < strlen( $utf8_text ) )
+                {
+                        // Cycle through the number of bytes specified,
+                        // copying them to the output string
+                        while ( $bytes > 0 )
+                        {
+                                $output .= $utf8_text{$pos};
+                                $pos++;
+                                $bytes--;
+                        }
+                }
+                else
+                {
+                        break;
+                }
+        }
+
+        // Return the result
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     UTF8_fix
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     UTF16_fix
+*
+* Description:  Checks a string for badly formed Unicode UTF-16 coding and
+*               returns the same string containing only the parts which
+*               were properly formed UTF-16 data.
+*
+* Parameters:   utf16_text - a string with possibly badly formed UTF-16 data
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      output - the well formed UTF-16 version of the string
+*
+******************************************************************************/
+
+function UTF16_fix( $utf16_text, $MSB_first )
+{
+        // Initialise the current position in the string
+        $pos = 0;
+
+        // Create a string to accept the well formed output
+        $output = "" ;
+
+        // Cycle through each group of bytes, ensuring the coding is correct
+        while ( $pos < strlen( $utf16_text ) )
+        {
+                // Retreive the current numerical character value
+                $chval1 = ord($utf16_text{$pos});
+
+                // Skip over character just read
+                $pos++;
+
+                // Check if there is another character available
+                if ( $pos  < strlen( $utf16_text ) )
+                {
+                        // Another character is available - get it for the second half of the UTF-16 value
+                        $chval2 = ord( $utf16_text{$pos} );
+                }
+                else
+                {
+                        // Error - no second byte to this UTF-16 value - end processing
+                        continue 1;
+                }
+
+                // Skip over character just read
+                $pos++;
+
+                // Calculate the 16 bit unicode value
+                if ( $MSB_first )
+                {
+                        // Big Endian
+                        $UTF16_val = $chval1 * 0x100 + $chval2;
+                }
+                else
+                {
+                        // Little Endian
+                        $UTF16_val = $chval2 * 0x100 + $chval1;
+                }
+
+
+
+                if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) ||
+                     ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) )
+                {
+                        // Normal Character (Non Surrogate pair)
+                        // Add it to the output
+                        $output .= chr( $chval1 ) . chr ( $chval2 );
+                }
+                else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) )
+                {
+                        // High surrogate of a surrogate pair
+                        // Now we need to read the low surrogate
+                        // Check if there is another 2 characters available
+                        if ( ( $pos + 3 ) < strlen( $utf16_text ) )
+                        {
+                                // Another 2 characters are available - get them
+                                $chval3 = ord( $utf16_text{$pos} );
+                                $chval4 = ord( $utf16_text{$pos+1} );
+
+                                // Calculate the second 16 bit unicode value
+                                if ( $MSB_first )
+                                {
+                                        // Big Endian
+                                        $UTF16_val2 = $chval3 * 0x100 + $chval4;
+                                }
+                                else
+                                {
+                                        // Little Endian
+                                        $UTF16_val2 = $chval4 * 0x100 + $chval3;
+                                }
+
+                                // Check that this is a low surrogate
+                                if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) )
+                                {
+                                        // Low surrogate found following high surrogate
+                                        // Add both to the output
+                                        $output .= chr( $chval1 ) . chr ( $chval2 ) . chr( $chval3 ) . chr ( $chval4 );
+
+                                        // Skip over the low surrogate
+                                        $pos += 2;
+                                }
+                                else
+                                {
+                                        // Low surrogate not found after high surrogate
+                                        // Don't add either to the output
+                                        // Only the High surrogate is skipped and processing continues after it
+                                }
+
+                        }
+                        else
+                        {
+                                // Error - not enough data for low surrogate - end processing
+                                continue 1;
+                        }
+
+                }
+                else
+                {
+                        // Low surrogate of a surrogate pair
+                        // This should not happen - it means this is a lone low surrogate
+                        // Dont add it to the output
+                }
+
+        }
+
+        // Return the result
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     UTF16_fix
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     UTF8_to_unicode_array
+*
+* Description:  Converts a string encoded with Unicode UTF-8, to an array of
+*               numbers which represent unicode character numbers
+*
+* Parameters:   utf8_text - a string containing the UTF-8 data
+*
+* Returns:      output - the array containing the unicode character numbers
+*
+******************************************************************************/
+
+function UTF8_to_unicode_array( $utf8_text )
+{
+        // Create an array to receive the unicode character numbers output
+        $output = array( );
+
+        // Cycle through the characters in the UTF-8 string
+        for ( $pos = 0; $pos < strlen( $utf8_text ); $pos++ )
+        {
+                // Retreive the current numerical character value
+                $chval = ord($utf8_text{$pos});
+
+                // Check what the first character is - it will tell us how many bytes the
+                // Unicode value covers
+
+                if ( ( $chval >= 0x00 ) && ( $chval <= 0x7F ) )
+                {
+                        // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character
+                        $bytes = 1;
+                        $outputval = $chval;    // Since 7-bit ASCII is unaffected, the output equals the input
+                }
+                else if ( ( $chval >= 0xC0 ) && ( $chval <= 0xDF ) )
+                {
+                        // 2 Byte UTF-8 Unicode
+                        $bytes = 2;
+                        $outputval = $chval & 0x1F;     // The first byte is bitwise ANDed with 0x1F to remove the leading 110b
+                }
+                else if ( ( $chval >= 0xE0 ) && ( $chval <= 0xEF ) )
+                {
+                        // 3 Byte UTF-8 Unicode
+                        $bytes = 3;
+                        $outputval = $chval & 0x0F;     // The first byte is bitwise ANDed with 0x0F to remove the leading 1110b
+                }
+                else if ( ( $chval >= 0xF0 ) && ( $chval <= 0xF7 ) )
+                {
+                        // 4 Byte UTF-8 Unicode
+                        $bytes = 4;
+                        $outputval = $chval & 0x07;     // The first byte is bitwise ANDed with 0x07 to remove the leading 11110b
+                }
+                else if ( ( $chval >= 0xF8 ) && ( $chval <= 0xFB ) )
+                {
+                        // 5 Byte UTF-8 Unicode
+                        $bytes = 5;
+                        $outputval = $chval & 0x03;     // The first byte is bitwise ANDed with 0x03 to remove the leading 111110b
+                }
+                else if ( ( $chval >= 0xFC ) && ( $chval <= 0xFD ) )
+                {
+                        // 6 Byte UTF-8 Unicode
+                        $bytes = 6;
+                        $outputval = $chval & 0x01;     // The first byte is bitwise ANDed with 0x01 to remove the leading 1111110b
+                }
+                else
+                {
+                        // Invalid Code - do nothing
+                        $bytes = 0;
+                }
+
+                // Check if the byte was valid
+                if ( $bytes !== 0 )
+                {
+                        // The byte was valid
+
+                        // Check if there is enough data left in the UTF-8 string to allow the
+                        // retrieval of the remainder of this unicode character
+                        if ( $pos + $bytes - 1 < strlen( $utf8_text ) )
+                        {
+                                // The UTF-8 string is long enough
+
+                                // Cycle through the number of bytes required,
+                                // minus the first one which has already been done
+                                while ( $bytes > 1 )
+                                {
+                                        $pos++;
+                                        $bytes--;
+
+                                        // Each remaining byte is coded with 6 bits of data and 10b on the high
+                                        // order bits. Hence we need to shift left by 6 bits (0x40) then add the
+                                        // current characer after it has been bitwise ANDed with 0x3F to remove the
+                                        // highest two bits.
+                                        $outputval = $outputval*0x40 + ( (ord($utf8_text{$pos})) & 0x3F );
+                                }
+
+                                // Add the calculated Unicode number to the output array
+                                $output[] = $outputval;
+                        }
+                }
+
+        }
+
+        // Return the resulting array
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     UTF8_to_unicode_array
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     UTF16_to_unicode_array
+*
+* Description:  Converts a string encoded with Unicode UTF-16, to an array of
+*               numbers which represent unicode character numbers
+*
+* Parameters:   utf16_text - a string containing the UTF-16 data
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      output - the array containing the unicode character numbers
+*
+******************************************************************************/
+
+function UTF16_to_unicode_array( $utf16_text, $MSB_first )
+{
+        // Create an array to receive the unicode character numbers output
+        $output = array( );
+
+
+        // Initialise the current position in the string
+        $pos = 0;
+
+        // Cycle through each group of bytes, ensuring the coding is correct
+        while ( $pos < strlen( $utf16_text ) )
+        {
+                // Retreive the current numerical character value
+                $chval1 = ord($utf16_text{$pos});
+
+                // Skip over character just read
+                $pos++;
+
+                // Check if there is another character available
+                if ( $pos  < strlen( $utf16_text ) )
+                {
+                        // Another character is available - get it for the second half of the UTF-16 value
+                        $chval2 = ord( $utf16_text{$pos} );
+                }
+                else
+                {
+                        // Error - no second byte to this UTF-16 value - end processing
+                        continue 1;
+                }
+
+                // Skip over character just read
+                $pos++;
+
+                // Calculate the 16 bit unicode value
+                if ( $MSB_first )
+                {
+                        // Big Endian
+                        $UTF16_val = $chval1 * 0x100 + $chval2;
+                }
+                else
+                {
+                        // Little Endian
+                        $UTF16_val = $chval2 * 0x100 + $chval1;
+                }
+
+
+                if ( ( ( $UTF16_val >= 0x0000 ) && ( $UTF16_val <= 0xD7FF ) ) ||
+                     ( ( $UTF16_val >= 0xE000 ) && ( $UTF16_val <= 0xFFFF ) ) )
+                {
+                        // Normal Character (Non Surrogate pair)
+                        // Add it to the output
+                        $output[] = $UTF16_val;
+                }
+                else if ( ( $UTF16_val >= 0xD800 ) && ( $UTF16_val <= 0xDBFF ) )
+                {
+                        // High surrogate of a surrogate pair
+                        // Now we need to read the low surrogate
+                        // Check if there is another 2 characters available
+                        if ( ( $pos + 3 ) < strlen( $utf16_text ) )
+                        {
+                                // Another 2 characters are available - get them
+                                $chval3 = ord( $utf16_text{$pos} );
+                                $chval4 = ord( $utf16_text{$pos+1} );
+
+                                // Calculate the second 16 bit unicode value
+                                if ( $MSB_first )
+                                {
+                                        // Big Endian
+                                        $UTF16_val2 = $chval3 * 0x100 + $chval4;
+                                }
+                                else
+                                {
+                                        // Little Endian
+                                        $UTF16_val2 = $chval4 * 0x100 + $chval3;
+                                }
+
+                                // Check that this is a low surrogate
+                                if ( ( $UTF16_val2 >= 0xDC00 ) && ( $UTF16_val2 <= 0xDFFF ) )
+                                {
+                                        // Low surrogate found following high surrogate
+                                        // Add both to the output
+                                        $output[] = 0x10000 + ( ( $UTF16_val - 0xD800 ) * 0x400 ) + ( $UTF16_val2 - 0xDC00 );
+
+                                        // Skip over the low surrogate
+                                        $pos += 2;
+                                }
+                                else
+                                {
+                                        // Low surrogate not found after high surrogate
+                                        // Don't add either to the output
+                                        // The high surrogate is skipped and processing continued
+                                }
+
+                        }
+                        else
+                        {
+                                // Error - not enough data for low surrogate - end processing
+                                continue 1;
+                        }
+
+                }
+                else
+                {
+                        // Low surrogate of a surrogate pair
+                        // This should not happen - it means this is a lone low surrogate
+                        // Don't add it to the output
+                }
+
+        }
+
+        // Return the result
+        return $output;
+
+
+}
+
+/******************************************************************************
+* End of Function:     UTF16_to_unicode_array
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     unicode_array_to_UTF8
+*
+* Description:  Converts an array of unicode character numbers to a string
+*               encoded by UTF-8
+*
+* Parameters:   unicode_array - the array containing unicode character numbers
+*
+* Returns:      output - the UTF-8 encoded string representing the data
+*
+******************************************************************************/
+
+function unicode_array_to_UTF8( $unicode_array )
+{
+
+        // Create a string to receive the UTF-8 output
+        $output = "";
+
+        // Cycle through each Unicode character number
+        foreach( $unicode_array as $unicode_char )
+        {
+                // Check which range the current unicode character lies in
+                if ( ( $unicode_char >= 0x00 ) && ( $unicode_char <= 0x7F ) )
+                {
+                        // 1 Byte UTF-8 Unicode (7-Bit ASCII) Character
+
+                        $output .= chr($unicode_char);          // Output is equal to input for 7-bit ASCII
+                }
+                else if ( ( $unicode_char >= 0x80 ) && ( $unicode_char <= 0x7FF ) )
+                {
+                        // 2 Byte UTF-8 Unicode - binary encode data as : 110xxxxx 10xxxxxx
+
+                        $output .= chr(0xC0 + ($unicode_char/0x40));
+                        $output .= chr(0x80 + ($unicode_char & 0x3F));
+                }
+                else if ( ( $unicode_char >= 0x800 ) && ( $unicode_char <= 0xFFFF ) )
+                {
+                        // 3 Byte UTF-8 Unicode - binary encode data as : 1110xxxx 10xxxxxx 10xxxxxx
+
+                        $output .= chr(0xE0 + ($unicode_char/0x1000));
+                        $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F));
+                        $output .= chr(0x80 + ($unicode_char & 0x3F));
+                }
+                else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x1FFFFF ) )
+                {
+                        // 4 Byte UTF-8 Unicode - binary encode data as : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+                        $output .= chr(0xF0 + ($unicode_char/0x40000));
+                        $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F));
+                        $output .= chr(0x80 + ($unicode_char & 0x3F));
+                }
+                else if ( ( $unicode_char >= 0x200000 ) && ( $unicode_char <= 0x3FFFFFF ) )
+                {
+                        // 5 Byte UTF-8 Unicode - binary encode data as : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+                        $output .= chr(0xF8 + ($unicode_char/0x1000000));
+                        $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F));
+                        $output .= chr(0x80 + ($unicode_char & 0x3F));
+                }
+                else if ( ( $unicode_char >= 0x4000000 ) && ( $unicode_char <= 0x7FFFFFFF ) )
+                {
+                        // 6 Byte UTF-8 Unicode - binary encode data as : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+                        $output .= chr(0xFC + ($unicode_char/0x40000000));
+                        $output .= chr(0x80 + (($unicode_char/0x1000000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x40000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x1000) & 0x3F));
+                        $output .= chr(0x80 + (($unicode_char/0x40) & 0x3F));
+                        $output .= chr(0x80 + ($unicode_char & 0x3F));
+                }
+                else
+                {
+                        // Invalid Code - do nothing
+                }
+
+        }
+
+        // Return resulting UTF-8 String
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     unicode_array_to_UTF8
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     unicode_array_to_UTF16
+*
+* Description:  Converts an array of unicode character numbers to a string
+*               encoded by UTF-16
+*
+* Parameters:   unicode_array - the array containing unicode character numbers
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      output - the UTF-16 encoded string representing the data
+*
+******************************************************************************/
+
+function unicode_array_to_UTF16( $unicode_array, $MSB_first )
+{
+
+        // Create a string to receive the UTF-16 output
+        $output = "";
+
+        // Cycle through each Unicode character number
+        foreach( $unicode_array as $unicode_char )
+        {
+                // Check which range the current unicode character lies in
+                if ( ( ( $unicode_char >= 0x0000 ) && ( $unicode_char <= 0xD7FF ) ) ||
+                     ( ( $unicode_char >= 0xE000 ) && ( $unicode_char <= 0xFFFF ) ) )
+                {
+                        // Normal 16 Bit Character  (Not a Surrogate Pair)
+
+                        // Check what byte order should be used
+                        if ( $MSB_first )
+                        {
+                                // Big Endian
+                                $output .= chr( $unicode_char / 0x100 ) . chr( $unicode_char % 0x100 ) ;
+                        }
+                        else
+                        {
+                                // Little Endian
+                                $output .= chr( $unicode_char % 0x100 ) . chr( $unicode_char / 0x100 ) ;
+                        }
+
+                }
+                else if ( ( $unicode_char >= 0x10000 ) && ( $unicode_char <= 0x10FFFF ) )
+                {
+                        // Surrogate Pair required
+
+                        // Calculate Surrogates
+                        $High_Surrogate = ( ( $unicode_char - 0x10000 ) / 0x400 ) + 0xD800;
+                        $Low_Surrogate = ( ( $unicode_char - 0x10000 ) % 0x400 ) + 0xDC00;
+
+                        // Check what byte order should be used
+                        if ( $MSB_first )
+                        {
+                                // Big Endian
+                                $output .= chr( $High_Surrogate / 0x100 ) . chr( $High_Surrogate % 0x100 );
+                                $output .= chr( $Low_Surrogate / 0x100 ) . chr( $Low_Surrogate % 0x100 );
+                        }
+                        else
+                        {
+                                // Little Endian
+                                $output .= chr( $High_Surrogate % 0x100 ) . chr( $High_Surrogate / 0x100 );
+                                $output .= chr( $Low_Surrogate % 0x100 ) . chr( $Low_Surrogate / 0x100 );
+                        }
+                }
+                else
+                {
+                        // Invalid UTF-16 codepoint
+                        // Unicode value should never be between 0xD800 and 0xDFFF
+                        // Do not output this point - there is no way to encode it in UTF-16
+                }
+
+        }
+
+        // Return resulting UTF-16 String
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     unicode_array_to_UTF16
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     xml_UTF8_clean
+*
+* Description:  XML has specific requirements about the characters that are
+*               allowed, and characters that must be escaped.
+*               This function ensures that all characters in the given string
+*               are valid, and that characters such as Quotes, Greater than,
+*               Less than and Ampersand are properly escaped. Newlines and Tabs
+*               are also escaped.
+*               Note - Do not use this on constructed XML which includes tags,
+*                      as it will escape the tags. It is designed to be used
+*                      on the tag and attribute names, attribute values, and text.
+*
+* Parameters:   utf8_text - a string containing the UTF-8 data
+*
+* Returns:      output - the array containing the unicode character numbers
+*
+******************************************************************************/
+
+function xml_UTF8_clean( $UTF8_text )
+{
+        // Ensure that the Unicode UTF8 encoding is valid.
+
+        $UTF8_text = UTF8_fix( $UTF8_text );
+
+
+        // XML only allows characters in the following unicode ranges
+        // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+        // Hence we need to delete any characters that dont fit this
+
+        // Convert the UTF-8 string to an array of unicode character numbers
+        $unicode_array = UTF8_to_unicode_array( $UTF8_text );
+
+        // Create a new array to receive the valid unicode character numbers
+        $new_unicode_array = array( );
+
+        // Cycle through the unicode character numbers
+        foreach( $unicode_array as  $unichar )
+        {
+                // Check if the unicode character number is valid for XML
+                if ( ( $unichar == 0x09 ) ||
+                     ( $unichar == 0x0A ) ||
+                     ( $unichar == 0x0D ) ||
+                     ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) ||
+                     ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) ||
+                     ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) )
+                {
+                       // Unicode character is valid for XML - add it to the valid characters array
+                       $new_unicode_array[] = $unichar;
+                }
+
+        }
+
+        // Convert the array of valid unicode character numbers back to UTF-8 encoded text
+        $UTF8_text = unicode_array_to_UTF8( $new_unicode_array );
+
+        // Escape any special HTML characters present
+        $UTF8_text =  htmlspecialchars ( $UTF8_text, ENT_QUOTES );
+
+        // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space
+        $trans = array( "\x09" => "&#x09;", "\x0A" => "&#x0A;", "\x0D" => "&#x0D;" );
+        $UTF8_text = strtr( $UTF8_text, $trans );
+
+        // Return the resulting XML valid string
+        return $UTF8_text;
+}
+
+/******************************************************************************
+* End of Function:     xml_UTF8_clean
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     xml_UTF16_clean
+*
+* Description:  XML has specific requirements about the characters that are
+*               allowed, and characters that must be escaped.
+*               This function ensures that all characters in the given string
+*               are valid, and that characters such as Quotes, Greater than,
+*               Less than and Ampersand are properly escaped. Newlines and Tabs
+*               are also escaped.
+*               Note - Do not use this on constructed XML which includes tags,
+*                      as it will escape the tags. It is designed to be used
+*                      on the tag and attribute names, attribute values, and text.
+*
+* Parameters:   utf16_text - a string containing the UTF-16 data
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      output - the array containing the unicode character numbers
+*
+******************************************************************************/
+
+function xml_UTF16_clean( $UTF16_text, $MSB_first )
+{
+        // Ensure that the Unicode UTF16 encoding is valid.
+
+        $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first );
+
+
+        // XML only allows characters in the following unicode ranges
+        // #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+        // Hence we need to delete any characters that dont fit this
+
+        // Convert the UTF-16 string to an array of unicode character numbers
+        $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first );
+
+        // Create a new array to receive the valid unicode character numbers
+        $new_unicode_array = array( );
+
+        // Cycle through the unicode character numbers
+        foreach( $unicode_array as  $unichar )
+        {
+                // Check if the unicode character number is valid for XML
+                if ( ( $unichar == 0x09 ) ||
+                     ( $unichar == 0x0A ) ||
+                     ( $unichar == 0x0D ) ||
+                     ( ( $unichar >= 0x20 ) && ( $unichar <= 0xD7FF ) ) ||
+                     ( ( $unichar >= 0xE000 ) && ( $unichar <= 0xFFFD ) ) ||
+                     ( ( $unichar >= 0x10000 ) && ( $unichar <= 0x10FFFF ) ) )
+                {
+                       // Unicode character is valid for XML - add it to the valid characters array
+                       $new_unicode_array[] = $unichar;
+                }
+
+        }
+
+        // Convert the array of valid unicode character numbers back to UTF-16 encoded text
+        $UTF16_text = unicode_array_to_UTF16( $new_unicode_array, $MSB_first );
+
+        // Escape any special HTML characters present
+        $UTF16_text =  htmlspecialchars ( $UTF16_text, ENT_QUOTES );
+
+        // Escape CR, LF and TAB characters, so that they are kept and not treated as expendable white space
+        $trans = array( "\x09" => "&#x09;", "\x0A" => "&#x0A;", "\x0D" => "&#x0D;" );
+        $UTF16_text = strtr( $UTF16_text, $trans );
+
+        // Return the resulting XML valid string
+        return $UTF16_text;
+}
+
+/******************************************************************************
+* End of Function:     xml_UTF16_clean
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     HTML_UTF8_Escape
+*
+* Description:  A HTML page can display UTF-8 data properly if it has a
+*               META http-equiv="Content-Type" tag with the content attribute
+*               including the value: "charset=utf-8".
+*               Otherwise the ISO-8859-1 character set is usually assumed, and
+*               Unicode values above 0x7F must be escaped.
+*               This function takes a UTF-8 encoded string and escapes the
+*               characters above 0x7F as well as reserved HTML characters such
+*               as Quotes, Greater than, Less than and Ampersand.
+*
+* Parameters:   utf8_text - a string containing the UTF-8 data
+*
+* Returns:      htmloutput - a string containing the HTML equivalent
+*
+******************************************************************************/
+
+function HTML_UTF8_Escape( $UTF8_text )
+{
+
+        // Ensure that the Unicode UTF8 encoding is valid.
+        $UTF8_text = UTF8_fix( $UTF8_text );
+
+        // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10
+        // Escape any special HTML characters present
+        $UTF8_text =  smart_htmlspecialchars( $UTF8_text, ENT_QUOTES );
+
+        // Convert the UTF-8 string to an array of unicode character numbers
+        $unicode_array = UTF8_to_unicode_array( $UTF8_text );
+
+        // Create a string to receive the escaped HTML
+        $htmloutput = "";
+
+        // Cycle through the unicode character numbers
+        foreach( $unicode_array as  $unichar )
+        {
+                // Check if the character needs to be escaped
+                if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) )
+                {
+                        // Character is less than 0x7F - add it to the html as is
+                        $htmloutput .= chr( $unichar );
+                }
+                else
+                {
+                        // Character is greater than 0x7F - escape it and add it to the html
+                        $htmloutput .= "&#x" . dechex($unichar) . ";";
+                }
+        }
+
+        // Return the resulting escaped HTML
+        return $htmloutput;
+}
+
+/******************************************************************************
+* End of Function:     HTML_UTF8_Escape
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     HTML_UTF8_UnEscape
+*
+* Description:  Converts HTML which contains escaped decimal or hex characters
+*               into UTF-8 text
+*
+* Parameters:   HTML_text - a string containing the HTML text to convert
+*
+* Returns:      utfoutput - a string containing the UTF-8 equivalent
+*
+******************************************************************************/
+
+function HTML_UTF8_UnEscape( $HTML_text )
+{
+        preg_match_all( "/\&\#(\d+);/", $HTML_text, $matches);
+        preg_match_all( "/\&\#[x|X]([A|B|C|D|E|F|a|b|c|d|e|f|0-9]+);/", $HTML_text, $hexmatches);
+        foreach( $hexmatches[1] as $index => $match )
+        {
+                $matches[0][] = $hexmatches[0][$index];
+                $matches[1][] = hexdec( $match );
+        }
+
+        for ( $i = 0; $i < count( $matches[ 0 ] ); $i++ )
+        {
+                $trans = array( $matches[0][$i] => unicode_array_to_UTF8( array( $matches[1][$i] ) ) );
+
+                $HTML_text = strtr( $HTML_text , $trans );
+        }
+        return $HTML_text;
+}
+
+/******************************************************************************
+* End of Function:     HTML_UTF8_UnEscape
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     HTML_UTF16_Escape
+*
+* Description:  A HTML page can display UTF-16 data properly if it has a
+*               META http-equiv="Content-Type" tag with the content attribute
+*               including the value: "charset=utf-16".
+*               Otherwise the ISO-8859-1 character set is usually assumed, and
+*               Unicode values above 0x7F must be escaped.
+*               This function takes a UTF-16 encoded string and escapes the
+*               characters above 0x7F as well as reserved HTML characters such
+*               as Quotes, Greater than, Less than and Ampersand.
+*
+* Parameters:   utf16_text - a string containing the UTF-16 data
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      htmloutput - a string containing the HTML equivalent
+*
+******************************************************************************/
+
+function HTML_UTF16_Escape( $UTF16_text, $MSB_first )
+{
+
+        // Ensure that the Unicode UTF16 encoding is valid.
+        $UTF16_text = UTF16_fix( $UTF16_text, $MSB_first );
+
+        // Change: changed to use smart_htmlspecialchars, so that characters which were already escaped would remain intact, as of revision 1.10
+        // Escape any special HTML characters present
+        $UTF16_text =  smart_htmlspecialchars( $UTF16_text );
+
+        // Convert the UTF-16 string to an array of unicode character numbers
+        $unicode_array = UTF16_to_unicode_array( $UTF16_text, $MSB_first );
+
+        // Create a string to receive the escaped HTML
+        $htmloutput = "";
+
+        // Cycle through the unicode character numbers
+        foreach( $unicode_array as  $unichar )
+        {
+                // Check if the character needs to be escaped
+                if ( ( $unichar >= 0x00 ) && ( $unichar <= 0x7F ) )
+                {
+                        // Character is less than 0x7F - add it to the html as is
+                        $htmloutput .= chr( $unichar );
+                }
+                else
+                {
+                        // Character is greater than 0x7F - escape it and add it to the html
+                        $htmloutput .= "&#x" . dechex($unichar) . ";";
+                }
+        }
+
+        // Return the resulting escaped HTML
+        return $htmloutput;
+}
+
+/******************************************************************************
+* End of Function:     HTML_UTF16_Escape
+******************************************************************************/
+
+
+/******************************************************************************
+*
+* Function:     HTML_UTF16_UnEscape
+*
+* Description:  Converts HTML which contains escaped decimal or hex characters
+*               into UTF-16 text
+*
+* Parameters:   HTML_text - a string containing the HTML text to be converted
+*               MSB_first - True will cause processing as Big Endian UTF-16 (Motorola, MSB first)
+*                           False will cause processing as Little Endian UTF-16 (Intel, LSB first)
+*
+* Returns:      utfoutput - a string containing the UTF-16 equivalent
+*
+******************************************************************************/
+
+function HTML_UTF16_UnEscape( $HTML_text, $MSB_first )
+{
+        $utf8_text = HTML_UTF8_UnEscape( $HTML_text );
+
+        return unicode_array_to_UTF16( UTF8_to_unicode_array( $utf8_text ), $MSB_first );
+}
+
+/******************************************************************************
+* End of Function:     HTML_UTF16_UnEscape
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     smart_HTML_Entities
+*
+* Description:  Performs the same function as HTML_Entities, but leaves entities
+*               that are already escaped intact.
+*
+* Parameters:   HTML_text - a string containing the HTML text to be escaped
+*
+* Returns:      HTML_text_out - a string containing the escaped HTML text
+*
+******************************************************************************/
+
+function smart_HTML_Entities( $HTML_text )
+{
+        // Get a table containing the HTML entities translations
+        $translation_table = get_html_translation_table( HTML_ENTITIES );
+
+        // Change the ampersand to translate to itself, to avoid getting &amp;
+        $translation_table[ chr(38) ] = '&';
+
+        // Perform replacements
+        // Regular expression says: find an ampersand, check the text after it,
+        // if the text after it is not one of the following, then replace the ampersand
+        // with &amp;
+        // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon
+        // b) a hash symbol, then between 2 and 7 digits
+        // c) a hash symbol, an 'x' character, then between 2 and 7 digits
+        // d) a hash symbol, an 'X' character, then between 2 and 7 digits
+        return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&amp;" , strtr( $HTML_text, $translation_table ) );
+}
+
+/******************************************************************************
+* End of Function:     smart_HTML_Entities
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     smart_htmlspecialchars
+*
+* Description:  Performs the same function as htmlspecialchars, but leaves characters
+*               that are already escaped intact.
+*
+* Parameters:   HTML_text - a string containing the HTML text to be escaped
+*
+* Returns:      HTML_text_out - a string containing the escaped HTML text
+*
+******************************************************************************/
+
+function smart_htmlspecialchars( $HTML_text )
+{
+        // Get a table containing the HTML special characters translations
+        $translation_table=get_html_translation_table (HTML_SPECIALCHARS);
+
+        // Change the ampersand to translate to itself, to avoid getting &amp;
+        $translation_table[ chr(38) ] = '&';
+
+        // Perform replacements
+        // Regular expression says: find an ampersand, check the text after it,
+        // if the text after it is not one of the following, then replace the ampersand
+        // with &amp;
+        // a) any combination of up to 4 letters (upper or lower case) with at least 2 or 3 non whitespace characters, then a semicolon
+        // b) a hash symbol, then between 2 and 7 digits
+        // c) a hash symbol, an 'x' character, then between 2 and 7 digits
+        // d) a hash symbol, an 'X' character, then between 2 and 7 digits
+        return preg_replace( "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,7}|#x[0-9]{2,7}|#X[0-9]{2,7};)/","&amp;" , strtr( $HTML_text, $translation_table ) );
+}
+
+/******************************************************************************
+* End of Function:     smart_htmlspecialchars
+******************************************************************************/
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/EXIF.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF.php	2005/01/20 22:34:16
@@ -0,0 +1,2756 @@
+<?
+
+/******************************************************************************
+*
+* Filename:     EXIF.php
+*
+* Description:  Provides functions for reading and writing EXIF Information
+*               to/from an APP1 segment of a JPEG file
+*               Unfortunately, because EXIF data may be distributed anywhere
+*               throughout an image file, rather than just being in one block,
+*               it is impossible to pass just a string containing only the EXIF
+*               information. Hence it is neccessary to be able to seek to
+*               any point in the file. This causes the HTTP and FTP wrappers
+*               not to work - i.e. the EXIF functions will only work with local
+*               files.
+*               To work on an internet file, copy it locally to start with:
+*
+*               $newfilename = tempnam ( $dir, "tmpexif" );
+*               copy ( "http://whatever.com", $newfilename );
+*
+*
+* Author:       Evan Hunter
+*
+* Date:         30/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.10 : added function get_EXIF_TIFF to allow extracting EXIF from a TIFF file
+*               1.10 -> 1.11 : added functionality to allow decoding of XMP and Photoshop IRB information
+*                              embedded within the EXIF data
+*                              added checks for http and ftp wrappers, as these are not supported
+*                              changed interpret_IFD to allow thumbnail links to work when
+*                              toolkit is portable across directories
+*
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// TODO : Thoroughly test the functions for writing EXIF segments
+// TODO : Figure out a way to allow EXIF to function normally with HTTP and FTP wrappers
+// TODO : Implement EXIF decoding of Device Setting Description field
+// TODO : Implement EXIF decoding of SpatialFrequencyResponse field
+// TODO : Implement EXIF decoding of OECF field
+// TODO : Implement EXIF decoding of SubjectArea field
+// TODO : Add a put_EXIF_TIFF function
+
+/******************************************************************************
+*
+* Initialisation
+*
+******************************************************************************/
+
+
+if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) )     $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
+if ( !isset( $GLOBALS['SHOW_BINARY_DATA_HEX'] ) )  $GLOBALS['SHOW_BINARY_DATA_HEX'] = FALSE;
+if ( !isset( $GLOBALS['SHOW_BINARY_DATA_TEXT'] ) ) $GLOBALS['SHOW_BINARY_DATA_TEXT'] = FALSE;
+
+
+include_once 'EXIF_Tags.php';
+include_once 'EXIF_Makernote.php';
+include_once 'PIM.php';
+include_once 'Unicode.php';
+include_once 'JPEG.php';
+include_once 'IPTC.php';
+include_once 'Photoshop_IRB.php';       // Change: as of version 1.11  - Required for TIFF with embedded IRB
+include_once 'XMP.php';                 // Change: as of version 1.11  - Required for TIFF with embedded XMP
+include_once 'pjmt_utils.php';          // Change: as of version 1.11  - Required for directory portability
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_EXIF_JPEG
+*
+* Description:  Retrieves information from a Exchangeable Image File Format (EXIF)
+*               APP1 segment and returns it in an array.
+*
+* Parameters:   filename - the filename of the JPEG image to process
+*
+* Returns:      OutputArray - Array of EXIF records
+*               FALSE - If an error occured in decoding
+*
+******************************************************************************/
+
+function get_EXIF_JPEG( $filename )
+{
+        // Change: Added as of version 1.11
+        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
+        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
+        {
+                // A HTTP or FTP wrapper is being used - show a warning and abort
+                echo "HTTP and FTP wrappers are currently not supported with EXIF - See EXIF functionality documentation - a local file must be specified<br>";
+                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
+                echo "\$newfilename = tempnam ( \$dir, \"tmpexif\" );<br>\n";
+                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
+                return FALSE;
+        }
+
+        // get the JPEG headers
+        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+
+        // Flag that an EXIF segment has not been found yet
+        $EXIF_Location = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP1 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
+                {
+                        // And if it has the EXIF label,
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) )          // For some reason, some files have a faulty EXIF name which has a 0xFF in it
+                        {
+                                // Save the location of the EXIF segment
+                                $EXIF_Location = $i;
+                        }
+                }
+
+        }
+
+        // Check if an EXIF segment was found
+        if ( $EXIF_Location == -1 )
+        {
+                // Couldn't find any EXIF block to decode
+                return FALSE;
+        }
+
+        $filehnd = @fopen($filename, 'rb');
+
+        // Check if the file opened successfully
+        if ( ! $filehnd  )
+        {
+                // Could't open the file - exit
+                echo "<p>Could not open file $filename</p>\n";
+                return FALSE;
+        }
+
+        fseek( $filehnd, $jpeg_header_data[$EXIF_Location]['SegDataStart'] + 6  );
+
+        // Decode the Exif segment into an array and return it
+        $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
+
+
+
+        // Close File
+        fclose($filehnd);
+        return $exif_data;
+}
+
+/******************************************************************************
+* End of Function:     get_EXIF_JPEG
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     put_EXIF_JPEG
+*
+* Description:  Stores information into a Exchangeable Image File Format (EXIF)
+*               APP1 segment from an EXIF array.
+*
+*               WARNING: Because the EXIF standard allows pointers to data
+*               outside the APP1 segment, if there are any such pointers in
+*               a makernote, this function will DAMAGE them since it will not
+*               be aware that there is an external pointer. This will often
+*               happen with Makernotes that include an embedded thumbnail.
+*               This damage could be prevented where makernotes can be decoded,
+*               but currently this is not implemented.
+*
+*
+* Parameters:   exif_data - The array of EXIF data to insert into the JPEG header
+*               jpeg_header_data - The JPEG header into which the EXIF data
+*                                  should be stored, as from get_jpeg_header_data
+*
+* Returns:      jpeg_header_data - JPEG header array with the EXIF segment inserted
+*               FALSE - If an error occured
+*
+******************************************************************************/
+
+function put_EXIF_JPEG( $exif_data, $jpeg_header_data )
+{
+        // pack the EXIF data into its proper format for a JPEG file
+        $packed_data = get_TIFF_Packed_Data( $exif_data );
+        if ( $packed_data === FALSE )
+        {
+                return $jpeg_header_data;
+        }
+
+        $packed_data = "Exif\x00\x00$packed_data";
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP1 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
+                {
+                        // And if it has the EXIF label,
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\x00", 6) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "Exif\x00\xFF", 6) == 0 ) )          // For some reason, some files have a faulty EXIF name which has a 0xFF in it
+                        {
+                                // Found a preexisting EXIF block - Replace it with the new one and return.
+                                $jpeg_header_data[$i]['SegData'] = $packed_data;
+                                return $jpeg_header_data;
+                        }
+                }
+        }
+
+        // No preexisting segment segment found, insert a new one at the start of the header data.
+
+        // Determine highest position of an APP segment at or below APP3, so we can put the
+        // new APP3 at this position
+
+
+        $highest_APP = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // Check if we have found an APP segment at or below APP3,
+                if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
+                {
+                        // Found an APP segment at or below APP12
+                        $highest_APP = $i;
+                }
+        }
+
+        // No preexisting EXIF block found, insert a new one at the start of the header data.
+        array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array(   "SegType" => 0xE1,
+                                                                              "SegName" => "APP1",
+                                                                              "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
+                                                                              "SegData" => $packed_data ) ) );
+        return $jpeg_header_data;
+
+}
+
+/******************************************************************************
+* End of Function:     put_EXIF_JPEG
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Meta_JPEG
+*
+* Description:  Retrieves information from a Meta APP3 segment and returns it
+*               in an array. Uses information supplied by the
+*               get_jpeg_header_data function.
+*               The Meta segment has the same format as an EXIF segment, but
+*               uses different tags
+*
+* Parameters:   filename - the filename of the JPEG image to process
+*
+* Returns:      OutputArray - Array of Meta records
+*               FALSE - If an error occured in decoding
+*
+******************************************************************************/
+
+function get_Meta_JPEG( $filename )
+{
+        // Change: Added as of version 1.11
+        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
+        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
+        {
+                // A HTTP or FTP wrapper is being used - show a warning and abort
+                echo "HTTP and FTP wrappers are currently not supported with Meta - See EXIF/Meta functionality documentation - a local file must be specified<br>";
+                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
+                echo "\$newfilename = tempnam ( \$dir, \"tmpmeta\" );<br>\n";
+                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
+                return FALSE;
+        }
+
+        // get the JPEG headers
+        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+
+        // Flag that an Meta segment has not been found yet
+        $Meta_Location = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP3 header,
+                if  ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
+                {
+                        // And if it has the Meta label,
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
+                        {
+                                // Save the location of the Meta segment
+                                $Meta_Location = $i;
+                        }
+                }
+        }
+
+        // Check if an EXIF segment was found
+        if ( $Meta_Location == -1 )
+        {
+                // Couldn't find any Meta block to decode
+                return FALSE;
+        }
+
+
+        $filehnd = @fopen($filename, 'rb');
+
+        // Check if the file opened successfully
+        if ( ! $filehnd  )
+        {
+                // Could't open the file - exit
+                echo "<p>Could not open file $filename</p>\n";
+                return FALSE;
+        }
+
+        fseek( $filehnd, $jpeg_header_data[$Meta_Location]['SegDataStart'] + 6 );
+
+        // Decode the Meta segment into an array and return it
+        $meta = process_TIFF_Header( $filehnd, "Meta" );
+
+         // Close File
+        fclose($filehnd);
+
+        return $meta;
+}
+
+/******************************************************************************
+* End of Function:     get_Meta
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_Meta_JPEG
+*
+* Description:  Stores information into a Meta APP3 segment from a Meta array.
+*
+*
+*               WARNING: Because the Meta (EXIF) standard allows pointers to data
+*               outside the APP1 segment, if there are any such pointers in
+*               a makernote, this function will DAMAGE them since it will not
+*               be aware that there is an external pointer. This will often
+*               happen with Makernotes that include an embedded thumbnail.
+*               This damage could be prevented where makernotes can be decoded,
+*               but currently this is not implemented.
+*
+*
+* Parameters:   meta_data - The array of Meta data to insert into the JPEG header
+*               jpeg_header_data - The JPEG header into which the Meta data
+*                                  should be stored, as from get_jpeg_header_data
+*
+* Returns:      jpeg_header_data - JPEG header array with the Meta segment inserted
+*               FALSE - If an error occured
+*
+******************************************************************************/
+
+function put_Meta_JPEG( $meta_data, $jpeg_header_data )
+{
+        // pack the Meta data into its proper format for a JPEG file
+        $packed_data = get_TIFF_Packed_Data( $meta_data );
+        if ( $packed_data === FALSE )
+        {
+                return $jpeg_header_data;
+        }
+
+        $packed_data = "Meta\x00\x00$packed_data";
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP1 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP3" ) == 0 )
+                {
+                        // And if it has the Meta label,
+                        if ( ( strncmp ( $jpeg_header_data[$i]['SegData'], "Meta\x00\x00", 6) == 0 ) ||
+                             ( strncmp ( $jpeg_header_data[$i]['SegData'], "META\x00\x00", 6) == 0 ) )
+                        {
+                                // Found a preexisting Meta block - Replace it with the new one and return.
+                                $jpeg_header_data[$i]['SegData'] = $packed_data;
+                                return $jpeg_header_data;
+                        }
+                }
+        }
+        // No preexisting segment segment found, insert a new one at the start of the header data.
+
+        // Determine highest position of an APP segment at or below APP3, so we can put the
+        // new APP3 at this position
+
+
+        $highest_APP = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // Check if we have found an APP segment at or below APP3,
+                if ( ( $jpeg_header_data[$i]['SegType'] >= 0xE0 ) && ( $jpeg_header_data[$i]['SegType'] <= 0xE3 ) )
+                {
+                        // Found an APP segment at or below APP12
+                        $highest_APP = $i;
+                }
+        }
+
+        // No preexisting Meta block found, insert a new one at the start of the header data.
+        array_splice($jpeg_header_data, $highest_APP + 1 , 0, array( array(     "SegType" => 0xE3,
+                                                                                "SegName" => "APP3",
+                                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
+                                                                                "SegData" => $packed_data ) ) );
+        return $jpeg_header_data;
+
+}
+
+/******************************************************************************
+* End of Function:     put_Meta_JPEG
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     get_EXIF_TIFF
+*
+* Description:  Retrieves information from a Exchangeable Image File Format (EXIF)
+*               within a TIFF file and returns it in an array.
+*
+* Parameters:   filename - the filename of the TIFF image to process
+*
+* Returns:      OutputArray - Array of EXIF records
+*               FALSE - If an error occured in decoding
+*
+******************************************************************************/
+
+function get_EXIF_TIFF( $filename )
+{
+        // Change: Added as of version 1.11
+        // Check if a wrapper is being used - these are not currently supported (see notes at top of file)
+        if ( ( stristr ( $filename, "http://" ) != FALSE ) || ( stristr ( $filename, "ftp://" ) != FALSE ) )
+        {
+                // A HTTP or FTP wrapper is being used - show a warning and abort
+                echo "HTTP and FTP wrappers are currently not supported with TIFF - See EXIF/TIFF functionality documentation - a local file must be specified<br>";
+                echo "To work on an internet file, copy it locally to start with:<br><br>\n";
+                echo "\$newfilename = tempnam ( \$dir, \"tmptiff\" );<br>\n";
+                echo "copy ( \"http://whatever.com\", \$newfilename );<br><br>\n";
+                return FALSE;
+        }
+
+
+        $filehnd = @fopen($filename, 'rb');
+
+        // Check if the file opened successfully
+        if ( ! $filehnd  )
+        {
+                // Could't open the file - exit
+                echo "<p>Could not open file $filename</p>\n";
+                return FALSE;
+        }
+
+        // Decode the Exif segment into an array and return it
+        $exif_data = process_TIFF_Header( $filehnd, "TIFF" );
+
+        // Close File
+        fclose($filehnd);
+        return $exif_data;
+}
+
+/******************************************************************************
+* End of Function:     get_EXIF_TIFF
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_EXIF_to_HTML
+*
+* Description:  Generates html detailing the contents an APP1 EXIF array
+*               which was retrieved with a get_EXIF_.... function.
+*               Can also be used for APP3 Meta arrays.
+*
+* Parameters:   Exif_array - the EXIF array,as read from get_EXIF_....
+*               filename - the name of the Image file being processed ( used
+*                          by scripts which displays EXIF thumbnails)
+*
+* Returns:      output_str - A string containing the HTML
+*
+******************************************************************************/
+
+function Interpret_EXIF_to_HTML( $Exif_array, $filename )
+{
+        // Create the string to receive the html output
+        $output_str = "";
+
+        // Check if the array to process is valid
+        if ( $Exif_array === FALSE )
+        {
+                // Exif Array is not valid - abort processing
+                return $output_str;
+        }
+
+        // Ouput the heading according to what type of tags were used in processing
+        if ( $Exif_array[ 'Tags Name' ] == "TIFF" )
+        {
+                $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains Exchangeable Image File Format (EXIF) Information</h2>\n";
+        }
+        else if ( $Exif_array[ 'Tags Name' ] == "Meta" )
+        {
+                $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains META Information (APP3)</h2>\n";
+        }
+        else
+        {
+                $output_str .= "<h2 class=\"EXIF_Main_Heading\">Contains " . $Exif_array[ 'Tags Name' ] . " Information</h2>\n";
+        }
+
+
+        // Check that there are actually items to process in the array
+        if ( count( $Exif_array ) < 1 )
+        {
+                // No items to process in array - abort processing
+                return $output_str;
+        }
+
+        // Output secondary heading
+        $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Main Image Information</h2>\n";
+
+        // Interpret the zeroth IFD to html
+        $output_str .= interpret_IFD( $Exif_array[0], $filename, $Exif_array['Byte_Align'] );
+
+        // Check if there is a first IFD to process
+        if ( array_key_exists( 1, $Exif_array ) )
+        {
+                // There is a first IFD for a thumbnail
+                // Add a heading for it to the output
+                $output_str .= "<h3 class=\"EXIF_Secondary_Heading\">Thumbnail Information</h2>\n";
+
+                // Interpret the IFD to html and add it to the output
+                $output_str .= interpret_IFD( $Exif_array[1], $filename, $Exif_array['Byte_Align'] );
+        }
+
+        // Cycle through any other IFD's
+        $i = 2;
+        while ( array_key_exists( $i, $Exif_array ) )
+        {
+                // Add a heading for the IFD
+                $output_str .= "<h3  class=\"EXIF_Secondary_Heading\">Image File Directory (IFD) $i Information</h2>\n";
+
+                // Interpret the IFD to html and add it to the output
+                $output_str .= interpret_IFD( $Exif_array[$i], $filename, $Exif_array['Byte_Align'] );
+                $i++;
+        }
+
+        // Return the resulting HTML
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_EXIF_to_HTML
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+*         INTERNAL FUNCTIONS
+*
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_TIFF_Packed_Data
+*
+* Description:  Packs TIFF IFD data from EXIF or Meta into a form ready for
+*               either a JPEG EXIF/Meta segment or a TIFF file
+*               This function attempts to protect the contents of an EXIF makernote,
+*               by ensuring that it remains in the same position relative to the
+*               TIFF header
+*
+* Parameters:   tiff_data - the EXIF array,as read from get_EXIF_JPEG or get_Meta_JPEG
+*
+* Returns:      packed_data - A string containing packed segment
+*
+******************************************************************************/
+
+function get_TIFF_Packed_Data( $tiff_data )
+{
+        // Check that the segment is valid
+        if ( $tiff_data === FALSE )
+        {
+                return FALSE;
+        }
+
+        // Get the byte alignment
+        $Byte_Align = $tiff_data['Byte_Align'];
+
+        // Add the Byte Alignment to the Packed data
+        $packed_data = $Byte_Align;
+
+        // Add the TIFF ID to the Packed Data
+        $packed_data .= put_IFD_Data_Type( 42, 3, $Byte_Align );
+
+        // Create a string for the makernote
+        $makernote = "";
+
+        // Check if the makernote exists
+        if ( $tiff_data[ 'Makernote_Tag' ] !== FALSE )
+        {
+                // A makernote exists - We need to ensure that it stays in the same position as it was
+                // Put the Makernote before any of the IFD's by padding zeros to the correct offset
+                $makernote .= str_repeat("\x00",( $tiff_data[ 'Makernote_Tag' ][ 'Offset' ] - 8 ) );
+                $makernote .= $tiff_data[ 'Makernote_Tag' ]['Data'];
+        }
+
+        // Calculage where the zeroth ifd will be
+        $ifd_offset = strlen( $makernote ) + 8;
+
+        // Add the Zeroth IFD pointer to the packed data
+        $packed_data .= put_IFD_Data_Type( $ifd_offset, 4, $Byte_Align );
+
+        // Add the makernote to the packed data (if there was one)
+        $packed_data .= $makernote;
+
+        //Add the IFD's to the packed data
+        $packed_data .= get_IFD_Array_Packed_Data( $tiff_data, $ifd_offset, $Byte_Align );
+
+        // Return the result
+        return $packed_data;
+}
+
+/******************************************************************************
+* End of Function:     get_TIFF_Packed_Data
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_IFD_Array_Packed_Data
+*
+* Description:  Packs a chain of IFD's from EXIF or Meta segments into a form
+*               ready for either a JPEG EXIF/Meta segment or a TIFF file
+*
+* Parameters:   ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
+*               Zero_IFD_offset - The offset to the first IFD from the start of the TIFF header
+*               Byte_Align - the Byte alignment to use - "MM" or "II"
+*
+* Returns:      packed_data - A string containing packed IFD's
+*
+******************************************************************************/
+
+function get_IFD_Array_Packed_Data( $ifd_data, $Zero_IFD_offset, $Byte_Align )
+{
+        // Create a string to receive the packed output
+        $packed_data = "";
+
+        // Count the IFDs
+        $ifd_count = 0;
+        foreach( $ifd_data as $key => $IFD )
+        {
+                // Make sure we only count the IFD's, not other information keys
+                if ( is_numeric( $key ) )
+                {
+                        $ifd_count++;
+                }
+        }
+
+
+        // Cycle through each IFD,
+        for ( $ifdno = 0; $ifdno < $ifd_count; $ifdno++ )
+        {
+                // Check if this IFD is the last one
+                if ( $ifdno == $ifd_count - 1 )
+                {
+                        // This IFD is the last one, get it's packed data
+                        $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, FALSE );
+                }
+                else
+                {
+                        // This IFD is NOT the last one, get it's packed data
+                        $packed_data .= get_IFD_Packed_Data( $ifd_data[ $ifdno ], $Zero_IFD_offset +strlen($packed_data), $Byte_Align, TRUE );
+                }
+
+        }
+
+        // Return the packed output
+        return $packed_data;
+}
+
+/******************************************************************************
+* End of Function:     get_IFD_Array_Packed_Data
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_IFD_Packed_Data
+*
+* Description:  Packs an IFD from EXIF or Meta segments into a form
+*               ready for either a JPEG EXIF/Meta segment or a TIFF file
+*
+* Parameters:   ifd_data - the IFD chain array, as read from get_EXIF_JPEG or get_Meta_JPEG
+*               IFD_offset - The offset to the IFD from the start of the TIFF header
+*               Byte_Align - the Byte alignment to use - "MM" or "II"
+*               Another_IFD - boolean - false if this is the last IFD in the chain
+*                                     - true if it is not the last
+*
+* Returns:      packed_data - A string containing packed IFD's
+*
+******************************************************************************/
+
+function get_IFD_Packed_Data( $ifd_data, $IFD_offset, $Byte_Align, $Another_IFD )
+{
+
+        $ifd_body_str = "";
+        $ifd_data_str = "";
+
+        $Tag_Definitions_Name = $ifd_data[ 'Tags Name' ];
+
+
+        // Count the Tags in this IFD
+        $tag_count = 0;
+        foreach( $ifd_data as $key => $tag )
+        {
+                // Make sure we only count the Tags, not other information keys
+                if ( is_numeric( $key ) )
+                {
+                        $tag_count++;
+                }
+        }
+
+        // Add the Tag count to the packed data
+        $packed_data = put_IFD_Data_Type( $tag_count, 3, $Byte_Align );
+
+        // Calculate the total length of the IFD (without the offset data)
+        $IFD_len = 2 + $tag_count * 12 + 4;
+
+
+        // Cycle through each tag
+        foreach( $ifd_data as $key => $tag )
+        {
+                // Make sure this is a tag, not another information key
+                if ( is_numeric( $key ) )
+                {
+
+                        // Add the tag number to the packed data
+                        $ifd_body_str .= put_IFD_Data_Type( $tag[ 'Tag Number' ], 3, $Byte_Align );
+
+                        // Add the Data type to the packed data
+                        $ifd_body_str .= put_IFD_Data_Type( $tag['Data Type'], 3, $Byte_Align );
+
+                        // Check if this is a Print Image Matching entry
+                        if ( $tag['Type'] == "PIM" )
+                        {
+                                // This is a Print Image Matching entry,
+                                // encode it
+                                $data = Encode_PIM( $tag, $Byte_Align );
+                        }
+                                // Check if this is a IPTC/NAA Record within the EXIF IFD
+                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                                  ( $tag[ 'Tag Number' ] == 33723 ) )
+                        {
+                                // This is a IPTC/NAA Record, encode it
+                                $data = put_IPTC( $tag['Data'] );
+                        }
+                                // Change: Check for embedded XMP as of version 1.11
+                                // Check if this is a XMP Record within the EXIF IFD
+                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                                  ( $tag[ 'Tag Number' ] == 700 ) )
+                        {
+                                // This is a XMP Record, encode it
+                                $data = write_XMP_array_to_text( $tag['Data'] );
+                        }
+                                // Change: Check for embedded IRB as of version 1.11
+                                // Check if this is a Photoshop IRB Record within the EXIF IFD
+                        else if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                                  ( $tag[ 'Tag Number' ] == 34377 ) )
+                        {
+                                // This is a Photoshop IRB Record, encode it
+                                $data = pack_Photoshop_IRB_Data( $tag['Data'] );
+                        }
+                                // Exif Thumbnail Offset
+                        else if ( ( $tag[ 'Tag Number' ] == 513 ) && ( $Tag_Definitions_Name == "TIFF" ) )
+                        {
+                                        // The Exif Thumbnail Offset is a pointer but of type Long, not Unknown
+                                        // Hence we need to put the data into the packed string separately
+                                        // Calculate the thumbnail offset
+                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
+
+                                        // Create the Offset for the IFD
+                                        $data = put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
+
+                                        // Store the thumbnail
+                                        $ifd_data_str .= $tag['Data'];
+                        }
+                                // Exif Thumbnail Length
+                        else if ( ( $tag[ 'Tag Number' ] == 514 ) && ( $Tag_Definitions_Name == "TIFF" ) )
+                        {
+                                        // Encode the Thumbnail Length
+                                        $data = put_IFD_Data_Type( strlen($ifd_data[513]['Data']), 4, $Byte_Align );
+                        }
+                                // Sub-IFD
+                        else if ( $tag['Type'] == "SubIFD" )
+                        {
+                                        // This is a Sub-IFD
+                                        // Calculate the offset to the start of the Sub-IFD
+                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
+                                        // Get the packed data for the IFD chain as the data for this tag
+                                        $data = get_IFD_Array_Packed_Data( $tag['Data'], $data_offset, $Byte_Align );
+                        }
+                        else
+                        {
+                                // Not a special tag
+
+                                // Create a string to receive the data
+                                $data = "";
+
+                                // Check if this is a type Unknown tag
+                                if ( $tag['Data Type'] != 7 )
+                                {
+                                        // NOT type Unknown
+                                        // Cycle through each data value and add it to the data string
+                                        foreach( $tag[ 'Data' ] as $data_val )
+                                        {
+                                                $data .= put_IFD_Data_Type( $data_val, $tag['Data Type'], $Byte_Align );
+                                        }
+                                }
+                                else
+                                {
+                                        // This is a type Unknown - just add the data as is to the data string
+                                        $data .= $tag[ 'Data' ];
+                                }
+                        }
+
+                        // Pad the data string out to at least 4 bytes
+                        $data = str_pad ( $data, 4, "\x00" );
+
+
+                        // Check if the data type is an ASCII String or type Unknown
+                        if ( ( $tag['Data Type'] == 2 ) || ( $tag['Data Type'] == 7 ) )
+                        {
+                                // This is an ASCII String or type Unknown
+                                // Add the Length of the string to the packed data as the Count
+                                $ifd_body_str .= put_IFD_Data_Type( strlen($data), 4, $Byte_Align );
+                        }
+                        else
+                        {
+                                // Add the array count to the packed data as the Count
+                                $ifd_body_str .= put_IFD_Data_Type( count($tag[ 'Data' ]), 4, $Byte_Align );
+                        }
+
+
+                        // Check if the data is over 4 bytes long
+                        if ( strlen( $data ) > 4 )
+                        {
+                                // Data is longer than 4 bytes - it needs to be offset
+                                // Check if this entry is the Maker Note
+                                if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $tag[ 'Tag Number' ] == 37500 ) )
+                                {
+                                        // This is the makernote - It will have already been stored
+                                        // at its original offset to help preserve it
+                                        // all we need to do is add the Offset to the IFD packed data
+                                        $data_offset = $tag[ 'Offset' ];
+
+                                        $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
+                                }
+                                else
+                                {
+                                        // This is NOT the makernote
+                                        // Calculate the data offset
+                                        $data_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
+
+                                        // Add the offset to the IFD packed data
+                                        $ifd_body_str .= put_IFD_Data_Type( $data_offset, 4, $Byte_Align );
+
+                                        // Add the data to the offset packed data
+                                        $ifd_data_str .= $data;
+                                }
+                        }
+                        else
+                        {
+                                // Data is less than or equal to 4 bytes - Add it to the packed IFD data as is
+                                $ifd_body_str .= $data;
+                        }
+
+                }
+        }
+
+        // Assemble the IFD body onto the packed data
+        $packed_data .= $ifd_body_str;
+
+        // Check if there is another IFD after this one
+        if( $Another_IFD === TRUE )
+        {
+                // There is another IFD after this
+                // Calculate the Next-IFD offset so that it goes immediately after this IFD
+                $next_ifd_offset = $IFD_offset + $IFD_len + strlen($ifd_data_str);
+        }
+        else
+        {
+                // There is NO IFD after this - indicate with offset=0
+                $next_ifd_offset = 0;
+        }
+
+        // Add the Next-IFD offset to the packed data
+        $packed_data .= put_IFD_Data_Type( $next_ifd_offset, 4, $Byte_Align );
+
+        // Add the offset data to the packed data
+        $packed_data .= $ifd_data_str;
+
+        // Return the resulting packed data
+        return $packed_data;
+}
+
+/******************************************************************************
+* End of Function:     get_IFD_Packed_Data
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     process_TIFF_Header
+*
+* Description:  Decodes the information stored in a TIFF header and it's
+*               Image File Directories (IFD's). This information is returned
+*               in an array
+*
+* Parameters:   filehnd - The handle of a open image file, positioned at the
+*                          start of the TIFF header
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      OutputArray - Array of IFD records
+*               FALSE - If an error occured in decoding
+*
+******************************************************************************/
+
+function process_TIFF_Header( $filehnd, $Tag_Definitions_Name )
+{
+
+
+        // Save the file position where the TIFF header starts, as offsets are relative to this position
+        $Tiff_start_pos = ftell( $filehnd );
+
+
+
+        // Read the eight bytes of the TIFF header
+        $DataStr = network_safe_fread( $filehnd, 8 );
+
+        // Check that we did get all eight bytes
+        if ( strlen( $DataStr ) != 8 )
+        {
+                return FALSE;   // Couldn't read the TIFF header properly
+        }
+
+        $pos = 0;
+        // First two bytes indicate the byte alignment - should be 'II' or 'MM'
+        // II = Intel (LSB first, MSB last - Little Endian)
+        // MM = Motorola (MSB first, LSB last - Big Endian)
+        $Byte_Align = substr( $DataStr, $pos, 2 );
+
+
+
+        // Check the Byte Align Characters for validity
+        if ( ( $Byte_Align != "II" ) && ( $Byte_Align != "MM" ) )
+        {
+                // Byte align field is invalid - we won't be able to decode file
+                return FALSE;
+        }
+
+        // Skip over the Byte Align field which was just read
+        $pos += 2;
+
+        // Next two bytes are TIFF ID - should be value 42 with the appropriate byte alignment
+        $TIFF_ID = substr( $DataStr, $pos, 2 );
+
+        if ( get_IFD_Data_Type( $TIFF_ID, 3, $Byte_Align ) != 42 )
+        {
+                // TIFF header ID not found
+                return FALSE;
+        }
+
+        // Skip over the TIFF ID field which was just read
+        $pos += 2;
+
+
+        // Next four bytes are the offset to the first IFD
+        $offset_str = substr( $DataStr, $pos, 4 );
+        $offset = get_IFD_Data_Type( $offset_str, 4, $Byte_Align );
+
+        // Done reading TIFF Header
+
+
+        // Move to first IFD
+
+        if ( fseek( $filehnd, $Tiff_start_pos + $offset ) !== 0 )
+        {
+                // Error seeking to position of first IFD
+                return FALSE;
+        }
+
+
+
+        // Flag that a makernote has not been found yet
+        $GLOBALS[ "Maker_Note_Tag" ] = FALSE;
+
+        // Read the IFD chain into an array
+        $Output_Array = read_Multiple_IFDs( $filehnd, $Tiff_start_pos, $Byte_Align, $Tag_Definitions_Name );
+
+        // Check if a makernote was found
+        if ( $GLOBALS[ "Maker_Note_Tag" ] != FALSE )
+        {
+                // Makernote was found - Process it
+                // The makernote needs to be processed after all other
+                // tags as it may require some of the other tags in order
+                // to be processed properly
+                $GLOBALS[ "Maker_Note_Tag" ] = Read_Makernote_Tag( $GLOBALS[ "Maker_Note_Tag" ], $Output_Array, $filehnd );
+
+        }
+
+        $Output_Array[ 'Makernote_Tag' ] = $GLOBALS[ "Maker_Note_Tag" ];
+
+        // Save the Name of the Tags used in the output array
+        $Output_Array[ 'Tags Name' ] = $Tag_Definitions_Name;
+
+
+
+        // Save the Byte alignment
+        $Output_Array['Byte_Align'] = $Byte_Align;
+
+
+        // Return the output array
+        return $Output_Array ;
+}
+
+/******************************************************************************
+* End of Function:     process_TIFF_Header
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     read_Multiple_IFDs
+*
+* Description:  Reads and interprets a chain of standard Image File Directories (IFD's),
+*               and returns the entries in an array. This chain is made up from IFD's
+*               which have a pointer to the next IFD. IFD's are read until the next
+*               pointer indicates there are no more
+*
+* Parameters:   filehnd - a handle for the image file being read, positioned at the
+*                         start of the IFD chain
+*               Tiff_offset - The offset of the TIFF header from the start of the file
+*               Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
+*               Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
+*               local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
+*                               False (normal) indicates offests are relative to start of Tiff header as per IFD standard
+*               read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
+*                               False indicates that no pointer follows the IFD
+*
+*
+* Returns:      OutputArray - Array of IFD entries
+*
+******************************************************************************/
+
+function read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
+{
+        // Start at the offset of the first IFD
+        $Next_Offset = 0;
+
+        do
+        {
+                // Read an IFD
+                list($IFD_Array , $Next_Offset) = read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets, $read_next_ptr );
+
+                // Move to the position of the next IFD
+                if ( fseek( $filehnd, $Tiff_offset + $Next_Offset ) !== 0 )
+                {
+                        // Error seeking to position of next IFD
+                        echo "<p>Error: Corrupted EXIF</p>\n";
+                        return FALSE;
+                }
+
+                $Output_Array[] = $IFD_Array;
+
+
+        } while ( $Next_Offset != 0 );      // Until the Next IFD Offset is zero
+
+
+        // return resulting array
+
+        return $Output_Array ;
+}
+
+/******************************************************************************
+* End of Function:     read_Multiple_IFDs
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     read_IFD_universal
+*
+* Description:  Reads and interprets a standard or Non-standard Image File
+*               Directory (IFD), and returns the entries in an array
+*
+* Parameters:   filehnd - a handle for the image file being read, positioned at the start
+*                         of the IFD
+*               Tiff_offset - The offset of the TIFF header from the start of the file
+*               Byte_Align - either "MM" or "II" indicating Motorola or Intel Byte alignment
+*               Tag_Definitions_Name - The name of the Tag Definitions group within the global array IFD_Tag_Definitions
+*               local_offsets - True indicates that offset data should be interpreted as being relative to the start of the currrent entry
+*                               False (normal) indicates offests are relative to start of Tiff header as per IFD standard
+*               read_next_ptr - True (normal) indicates that a pointer to the next IFD should be read at the end of the IFD
+*                               False indicates that no pointer follows the IFD
+*
+* Returns:      OutputArray - Array of IFD entries
+*               Next_Offset - Offset to next IFD (zero = no next IFD)
+*
+******************************************************************************/
+
+function read_IFD_universal( $filehnd, $Tiff_offset, $Byte_Align, $Tag_Definitions_Name, $local_offsets = FALSE, $read_next_ptr = TRUE )
+{
+        if ( ( $filehnd == NULL ) || ( feof( $filehnd ) ) )
+        {
+                return array (FALSE , 0);
+        }
+
+        // Record the Name of the Tag Group used for this IFD in the output array
+        $OutputArray[ 'Tags Name' ] = $Tag_Definitions_Name;
+
+        // Record the offset of the TIFF header in the output array
+        $OutputArray[ 'Tiff Offset' ] = $Tiff_offset;
+
+        // First 2 bytes of IFD are number of entries in the IFD
+        $No_Entries_str = network_safe_fread( $filehnd, 2 );
+        $No_Entries = get_IFD_Data_Type( $No_Entries_str, 3, $Byte_Align );
+
+
+        // If the data is corrupt, the number of entries may be huge, which will cause errors
+        // This is often caused by a lack of a Next-IFD pointer
+        if ( $No_Entries> 10000 )
+        {
+                // Huge number of entries - abort
+                echo "<p>Error: huge number of EXIF entries - EXIF is probably Corrupted</p>\n";
+
+                return array ( FALSE , 0);
+        }
+
+        // If the data is corrupt or just stupid, the number of entries may zero,
+        // Indicate this by returning false
+        if ( $No_Entries === 0 )
+        {
+                // No entries - abort
+                return array ( FALSE , 0);
+        }
+
+        // Save the file position where first IFD record starts as non-standard offsets
+        // need to know this to calculate an absolute offset
+        $IFD_first_rec_pos = ftell( $filehnd );
+
+
+        // Read in the IFD structure
+        $IFD_Data = network_safe_fread( $filehnd, 12 * $No_Entries );
+
+        // Check if the entire IFD was able to be read
+        if ( strlen( $IFD_Data ) != (12 * $No_Entries) )
+        {
+                // Couldn't read the IFD Data properly, Some Casio files have no Next IFD pointer, hence cause this error
+                echo "<p>Error: EXIF Corrupted</p>\n";
+                return array(FALSE, 0);
+        }
+
+
+        // Last 4 bytes of a standard IFD are the offset to the next IFD
+        // Some NON-Standard IFD implementations do not have this, hence causing problems if it is read
+
+        // If the Next IFD pointer has been requested to be read,
+        if ( $read_next_ptr )
+        {
+                // Read the pointer to the next IFD
+
+                $Next_Offset_str = network_safe_fread( $filehnd, 4 );
+                $Next_Offset = get_IFD_Data_Type( $Next_Offset_str, 4, $Byte_Align );
+        }
+        else
+        {
+                // Otherwise set the pointer to zero ( no next IFD )
+                $Next_Offset = 0;
+        }
+
+
+
+        // Initialise current position to the start
+        $pos = 0;
+
+
+        // Loop for reading IFD entries
+
+        for ( $i = 0; $i < $No_Entries; $i++ )
+        {
+                // First 2 bytes of IFD entry are the tag number ( Unsigned Short )
+                $Tag_No_str = substr( $IFD_Data, $pos, 2 );
+                $Tag_No = get_IFD_Data_Type( $Tag_No_str, 3, $Byte_Align );
+                $pos += 2;
+
+                // Next 2 bytes of IFD entry are the data format ( Unsigned Short )
+                $Data_Type_str = substr( $IFD_Data, $pos, 2 );
+                $Data_Type = get_IFD_Data_Type( $Data_Type_str, 3, $Byte_Align );
+                $pos += 2;
+
+                // If Datatype is not between 1 and 12, then skip this entry, it is probably corrupted or custom
+                if (( $Data_Type > 12 ) || ( $Data_Type < 1 ) )
+                {
+                        $pos += 8;
+                        continue 1;  // Stop trying to process the tag any further and skip to the next one
+                }
+
+                // Next 4 bytes of IFD entry are the data count ( Unsigned Long )
+                $Data_Count_str = substr( $IFD_Data, $pos, 4 );
+                $Data_Count = get_IFD_Data_Type( $Data_Count_str, 4, $Byte_Align );
+                $pos += 4;
+
+                if ( $Data_Count > 100000 )
+                {
+                        echo "<p>Error: huge EXIF data count - EXIF is probably Corrupted</p>\n";
+
+                        // Some Casio files have no Next IFD pointer, hence cause errors
+
+                        return array ( FALSE , 0);
+                }
+
+                // Total Data size is the Data Count multiplied by the size of the Data Type
+                $Total_Data_Size = $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] * $Data_Count;
+
+                $Data_Start_pos = -1;
+
+                // If the total data size is larger than 4 bytes, then the data part is the offset to the real data
+                if ( $Total_Data_Size > 4 )
+                {
+                        // Not enough room for data - offset provided instead
+                        $Data_Offset_str = substr( $IFD_Data, $pos, 4 );
+                        $Data_Start_pos = get_IFD_Data_Type( $Data_Offset_str, 4, $Byte_Align );
+
+
+                        // In some NON-STANDARD makernotes, the offset is relative to the start of the current IFD entry
+                        if ( $local_offsets )
+                        {
+                                // This is a NON-Standard IFD, seek relative to the start of the current tag
+                                fseek( $filehnd, $IFD_first_rec_pos +  $pos - 8 + $Data_Start_pos );
+                        }
+                        else
+                        {
+                                // This is a normal IFD, seek relative to the start of the TIFF header
+                                fseek( $filehnd, $Tiff_offset + $Data_Start_pos );
+                        }
+
+                        // Read the data block from the offset position
+                        $DataStr = network_safe_fread( $filehnd, $Total_Data_Size );
+                }
+                else
+                {
+                        // The data block is less than 4 bytes, and is provided in the IFD entry, so read it
+                        $DataStr = substr( $IFD_Data, $pos, $Total_Data_Size );
+                }
+
+                // Increment the position past the data
+                $pos += 4;
+
+
+                // Now create the entry for output array
+
+                $Data_Array = array( );
+
+
+                // Read the data items from the data block
+
+                if ( ( $Data_Type != 2 ) && ( $Data_Type != 7 ) )
+                {
+                        // The data type is Numerical, Read the data items from the data block
+                        for ( $j = 0; $j < $Data_Count; $j++ )
+                        {
+                                $Part_Data_Str = substr( $DataStr, $j * $GLOBALS['IFD_Data_Sizes'][ $Data_Type ], $GLOBALS['IFD_Data_Sizes'][ $Data_Type ] );
+                                $Data_Array[] = get_IFD_Data_Type( $Part_Data_Str, $Data_Type, $Byte_Align );
+                        }
+                }
+                elseif ( $Data_Type == 2 )
+                {
+                        // The data type is String(s)   (type 2)
+
+                        // Strip the last terminating Null
+                        $DataStr = substr( $DataStr, 0, strlen($DataStr)-1 );
+
+                        // Split the data block into multiple strings whereever there is a Null
+                        $Data_Array = explode( "\x00", $DataStr );
+                }
+                else
+                {
+                        // The data type is Unknown (type 7)
+                        // Do nothing to data
+                        $Data_Array = $DataStr;
+                }
+
+
+                // If this is a Sub-IFD entry,
+                if ( ( array_key_exists( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name] ) ) &&
+                     ( "SubIFD" == $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'] ) )
+                {
+                        // This is a Sub-IFD entry, go and process the data forming Sub-IFD and use its output array as the new data for this entry
+                        fseek( $filehnd, $Tiff_offset + $Data_Array[0] );
+                        $Data_Array = read_Multiple_IFDs( $filehnd, $Tiff_offset, $Byte_Align, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Tags Name'] );
+                }
+
+                $desc = "";
+                $units = "";
+
+                // Check if this tag exists in the list of tag definitions,
+
+                if ( array_key_exists ( $Tag_No, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name]) )
+                {
+
+                        if ( array_key_exists ( 'Description', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
+                        {
+                                $desc = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Description'];
+                        }
+
+                        if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ] ) )
+                        {
+                                $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Units'];
+                        }
+
+                        // Tag exists in definitions, append details to output array
+                        $OutputArray[ $Tag_No ] = array (       "Tag Number"      => $Tag_No,
+                                                                "Tag Name"        => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Name'],
+                                                                "Tag Description" => $desc,
+                                                                "Data Type"       => $Data_Type,
+                                                                "Type"            => $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag_No ]['Type'],
+                                                                "Units"           => $units,
+                                                                "Data"            => $Data_Array );
+
+                }
+                else
+                {
+                        // Tag doesnt exist in definitions, append unknown details to output array
+
+                        $OutputArray[ $Tag_No ] = array (       "Tag Number"      => $Tag_No,
+                                                                "Tag Name"        => "Unknown Tag #" . $Tag_No,
+                                                                "Tag Description" => "",
+                                                                "Data Type"       => $Data_Type,
+                                                                "Type"            => "Unknown",
+                                                                "Units"           => "",
+                                                                "Data"            => $Data_Array );
+                }
+
+
+
+                // Some information of type "Unknown" (type 7) might require information about
+                // how it's position and byte alignment in order to be decoded
+                if ( $Data_Type == 7 )
+                {
+                        $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
+                        $OutputArray[ $Tag_No ]['Byte Align'] = $Byte_Align;
+                }
+
+
+                ////////////////////////////////////////////////////////////////////////
+                // Special Data handling
+                ////////////////////////////////////////////////////////////////////////
+
+
+                // Check if this is a Print Image Matching entry
+                if ( $OutputArray[ $Tag_No ]['Type'] == "PIM" )
+                {
+                        // This is a Print Image Matching entry, decode it.
+                        $OutputArray[ $Tag_No ] = Decode_PIM( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
+                }
+
+
+                // Interpret the entry into a text string using a custom interpreter
+                $text_val = get_Tag_Text_Value( $OutputArray[ $Tag_No ], $Tag_Definitions_Name );
+
+                // Check if a text string was generated
+                if ( $text_val !== FALSE )
+                {
+                        // A string was generated, append it to the output array entry
+                        $OutputArray[ $Tag_No ]['Text Value'] = $text_val;
+                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
+                }
+                else
+                {
+                        // A string was NOT generated, append a generic string to the output array entry
+                        $OutputArray[ $Tag_No ]['Text Value'] = get_IFD_value_as_text( $OutputArray[ $Tag_No ] )  . " " . $units;
+                        $OutputArray[ $Tag_No ]['Decoded'] = FALSE;
+                }
+
+
+
+
+                // Check if this entry is the Maker Note
+                if ( ( $Tag_Definitions_Name == "EXIF" ) && ( $Tag_No == 37500 ) )
+                {
+
+                        // Save some extra information which will allow Makernote Decoding with the output array entry
+                        $OutputArray[ $Tag_No ]['Offset'] = $Data_Start_pos;
+                        $OutputArray[ $Tag_No ][ 'Tiff Offset' ] = $Tiff_offset;
+                        $OutputArray[ $Tag_No ]['ByteAlign'] = $Byte_Align;
+
+                        // Save a pointer to this entry for Maker note processing later
+                        $GLOBALS[ "Maker_Note_Tag" ] = & $OutputArray[ $Tag_No ];
+                }
+
+
+                // Check if this is a IPTC/NAA Record within the EXIF IFD
+                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                     ( $Tag_No == 33723 ) )
+                {
+                        // This is a IPTC/NAA Record, interpret it and put result in the data for this entry
+                        $OutputArray[ $Tag_No ]['Data'] = get_IPTC( $DataStr );
+                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
+                }
+                // Change: Check for embedded XMP as of version 1.11
+                // Check if this is a XMP Record within the EXIF IFD
+                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                     ( $Tag_No == 700 ) )
+                {
+                        // This is a XMP Record, interpret it and put result in the data for this entry
+                        $OutputArray[ $Tag_No ]['Data'] =  read_XMP_array_from_text( $DataStr );
+                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
+                }
+
+                // Change: Check for embedded IRB as of version 1.11
+                // Check if this is a Photoshop IRB Record within the EXIF IFD
+                if ( ( ( $Tag_Definitions_Name == "EXIF" ) || ( $Tag_Definitions_Name == "TIFF" ) ) &&
+                     ( $Tag_No == 34377 ) )
+                {
+                        // This is a Photoshop IRB Record, interpret it and put result in the data for this entry
+                        $OutputArray[ $Tag_No ]['Data'] = unpack_Photoshop_IRB_Data( $DataStr );
+                        $OutputArray[ $Tag_No ]['Decoded'] = TRUE;
+                }
+
+                // Exif Thumbnail
+                // Check that both the thumbnail length and offset entries have been processed,
+                // and that this is one of them
+                if ( ( ( ( $Tag_No == 513 ) && ( array_key_exists( 514, $OutputArray ) ) ) ||
+                       ( ( $Tag_No == 514 ) && ( array_key_exists( 513, $OutputArray ) ) ) )  &&
+                     ( $Tag_Definitions_Name == "TIFF" ) )
+                {
+                        // Seek to the start of the thumbnail using the offset entry
+                        fseek( $filehnd, $Tiff_offset + $OutputArray[513]['Data'][0] );
+
+                        // Read the thumbnail data, and replace the offset data with the thumbnail
+                        $OutputArray[513]['Data'] = network_safe_fread( $filehnd, $OutputArray[514]['Data'][0] );
+                }
+
+
+                // Casio Thumbnail
+                // Check that both the thumbnail length and offset entries have been processed,
+                // and that this is one of them
+                if ( ( ( ( $Tag_No == 0x0004 ) && ( array_key_exists( 0x0003, $OutputArray ) ) ) ||
+                       ( ( $Tag_No == 0x0003 ) && ( array_key_exists( 0x0004, $OutputArray ) ) ) )  &&
+                     ( $Tag_Definitions_Name == "Casio Type 2" ) )
+                {
+                        // Seek to the start of the thumbnail using the offset entry
+                        fseek( $filehnd, $Tiff_offset + $OutputArray[0x0004]['Data'][0] );
+
+                        // Read the thumbnail data, and replace the offset data with the thumbnail
+                        $OutputArray[0x0004]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0003]['Data'][0] );
+                }
+
+                // Minolta Thumbnail
+                // Check that both the thumbnail length and offset entries have been processed,
+                // and that this is one of them
+                if ( ( ( ( $Tag_No == 0x0088 ) && ( array_key_exists( 0x0089, $OutputArray ) ) ) ||
+                       ( ( $Tag_No == 0x0089 ) && ( array_key_exists( 0x0088, $OutputArray ) ) ) )  &&
+                     ( $Tag_Definitions_Name == "Olympus" ) )
+                {
+
+                        // Seek to the start of the thumbnail using the offset entry
+                        fseek( $filehnd, $Tiff_offset + $OutputArray[0x0088]['Data'][0] );
+
+                        // Read the thumbnail data, and replace the offset data with the thumbnail
+                        $OutputArray[0x0088]['Data'] = network_safe_fread( $filehnd, $OutputArray[0x0089]['Data'][0] );
+
+                        // Sometimes the minolta thumbnail data is empty (or the offset is corrupt, which results in the same thing)
+
+                        // Check if the thumbnail data exists
+                        if ( $OutputArray[0x0088]['Data'] != "" )
+                        {
+                                // Thumbnail exists
+
+                                // Minolta Thumbnails are missing their first 0xFF for some reason,
+                                // which is replaced with some weird character, so fix this
+                                $OutputArray[0x0088]['Data']{0} = "\xFF";
+                        }
+                        else
+                        {
+                                // Thumbnail doesnt exist - make it obvious
+                                $OutputArray[0x0088]['Data'] = FALSE;
+                        }
+                }
+
+        }
+
+
+
+
+
+
+
+        // Return the array of IFD entries and the offset to the next IFD
+
+        return array ($OutputArray , $Next_Offset);
+}
+
+
+
+/******************************************************************************
+* End of Function:     read_IFD_universal
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_Tag_Text_Value
+*
+* Description:  Attempts to interpret an IFD entry into a text string using the
+*               information in the IFD_Tag_Definitions global array.
+*
+* Parameters:   Tag - The IFD entry to process
+*               Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
+*
+* Returns:      String - if the tag was successfully decoded into a text string
+*               FALSE - if the tag could not be decoded using the information
+*                       in the IFD_Tag_Definitions global array
+*
+******************************************************************************/
+
+function get_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
+{
+        // Check what format the entry is specified as
+
+        if ( $Tag['Type'] == "String" )
+        {
+                // Format is Text String
+
+                // If "Unknown" (type 7) data type,
+                if ( $Tag['Data Type'] == 7 )
+                {
+                        // Return data as is.
+                        return $Tag['Data'];
+                }
+                else
+                {
+                        // Otherwise return the default string value of the datatype
+                        return get_IFD_value_as_text( $Tag );
+                }
+        }
+        else if ( $Tag['Type'] == "Character Coded String" )
+        {
+                // Format is Character Coded String (First 8 characters indicate coding scheme)
+
+                // Convert Data to a string
+                if ( $Tag['Data Type'] == 7 )
+                {
+                        // If it is type "Unknown" (type 7) use data as is
+                        $data =  $Tag['Data'];
+                }
+                else
+                {
+                        // Otherwise use the default string value of the datatype
+                        $data = get_IFD_value_as_text( $Tag );
+                }
+
+                // Some implementations allow completely data with no Coding Scheme Name,
+                // so we need to handle this to avoid errors
+                if ( trim( $data ) == "" )
+                {
+                        return "";
+                }
+
+                // Extract the Coding Scheme Name from the first 8 characters
+                $char_code = substr( $data, 0, 8 );
+
+                // Extract the Data part from after the first 8 characters
+                $characters = substr( $data, 8 );
+
+                // Check coding scheme and interpret as neccessary
+
+                if ( $char_code === "ASCII\x00\x00\x00" )
+                {
+                        // ASCII coding - return data as is.
+                        return $characters;
+                }
+                elseif ( ( $char_code === "UNICODE\x00" ) ||
+                         ( $char_code === "Unicode\x00" ) )             // Note lowercase is non standard
+                {
+                        // Unicode coding - interpret and return result.
+                        return xml_UTF16_clean( $characters, TRUE );
+                }
+                else
+                {
+                        // Unknown coding - return string indicating this
+                        return "Unsupported character coding : \"$char_code\"\n\"" . trim($characters) . "\"";
+                }
+                break;
+        }
+        else if ( $Tag['Type'] == "Numeric" )
+        {
+                // Format is numeric - return default text value with any required units text appended
+                if ( array_key_exists ( 'Units', $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
+                {
+                        $units = $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ]['Units'];
+                }
+                else
+                {
+                        $units = "";
+                }
+                return get_IFD_value_as_text( $Tag )  . " " . $units;
+        }
+        else if  ( $Tag['Type'] == "Lookup" )
+        {
+                // Format is a Lookup Table
+
+                // Get a numeric value to use in lookup
+
+                if ( is_array( $Tag['Data'] ) )
+                {
+                        // If data is an array, use first element
+                        $first_val = $Tag['Data'][0];
+                }
+                else if ( is_string( $Tag['Data'] ) )
+                {
+                        // If data is a string, use the first character
+                        $first_val = ord($Tag['Data']{0});
+                }
+                else
+                {
+                        // Otherwise use the data as is
+                        $first_val = $Tag['Data'];
+                }
+
+                // Check if the data value exists in the lookup table for this IFD entry
+                if ( array_key_exists( $first_val, $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ] ) )
+                {
+                        // Data value exists in lookup table - return the matching string
+                        return $GLOBALS[ "IFD_Tag_Definitions" ][$Tag_Definitions_Name][ $Tag["Tag Number"] ][ $first_val ];
+                }
+                else
+                {
+                        // Data value doesnt exist in lookup table - return explanation string
+                        return "Unknown Reserved value $first_val ";
+                }
+        }
+        else if  ( $Tag['Type'] == "Special" )
+        {
+                // Format is special - interpret to text with special handlers
+                return get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name );
+        }
+        else if  ( $Tag['Type'] == "PIM" )
+        {
+                // Format is Print Image Matching info - interpret with custom handler
+                return get_PIM_Text_Value( $Tag, $Tag_Definitions_Name );
+        }
+        else if  ( $Tag['Type'] == "SubIFD" )
+        {
+                // Format is a Sub-IFD - this has no text value
+                return "";
+        }
+        else
+        {
+                // Unknown Format - Couldn't interpret using the IFD_Tag_Definitions global array information
+                return FALSE;
+        }
+}
+
+/******************************************************************************
+* End of Function:     get_Tag_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_Special_Tag_Text_Value
+*
+* Description:  Interprets an IFD entry marked as "Special" in the IFD_Tag_Definitions
+*               global array into a text string using custom handlers
+*
+* Parameters:   Tag - The IFD entry to process
+*               Tag_Definitions_Name - The name of the tag definitions to use from within the IFD_Tag_Definitions global array
+*
+* Returns:      String - if the tag was successfully decoded into a text string
+*               FALSE - if the tag could not be decoded
+*
+******************************************************************************/
+
+function get_Special_Tag_Text_Value( $Tag, $Tag_Definitions_Name )
+{
+        // Check what type of IFD is being decoded
+
+        if ( $Tag_Definitions_Name == "TIFF" )
+        {
+                // This is a TIFF IFD (bottom level)
+
+                // Check what tag number the IFD entry has.
+                switch ( $Tag['Tag Number'] )
+                {
+                        case 530:  // YCbCr Sub Sampling Entry
+
+                                // Data contains two numerical values
+
+                                if ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 1 ) )
+                                {
+                                        // Values are 2,1 - hence YCbCr 4:2:2
+                                        return "YCbCr 4:2:2 ratio of chrominance components to the luminance components";
+                                }
+                                elseif ( ( $Tag['Data'][0] == 2 ) && ( $Tag['Data'][1] == 2 ) )
+                                {
+                                        // Values are 2,2 - hence YCbCr 4:2:0
+                                        return "YCbCr 4:2:0 ratio of chrominance components to the luminance components";
+                                }
+                                else
+                                {
+                                        // Other values are unknown
+                                        return "Unknown Reserved value (" . $Tag['Data'][0] . ")";
+                                }
+                                break;
+
+                        default:
+                                return FALSE;
+                }
+        }
+        else if ( $Tag_Definitions_Name == "EXIF" )
+        {
+                // This is an EXIF IFD
+
+                // Check what tag number the IFD entry has.
+                switch ( $Tag['Tag Number'] )
+                {
+
+                        case 37121: // Components configuration
+
+                                // Data contains 4 numerical values indicating component type
+
+                                $output_str = "";
+
+                                // Cycle through each component
+                                for ( $Num = 0; $Num < 4; $Num++ )
+                                {
+                                        // Construct first part of text string
+                                        $output_str .= "Component " . ( $Num + 1 ) . ": ";
+
+                                        // Construct second part of text string via
+                                        // lookup using numerical value
+
+                                        $value = ord( $Tag['Data']{$Num} );
+                                        switch( $value )
+                                        {
+                                                case 0:
+                                                        $output_str .= "Does not exist\n";
+                                                        break;
+                                                case 1:
+                                                        $output_str .= "Y (Luminance)\n";
+                                                        break;
+                                                case 2:
+                                                        $output_str .= "Cb (Chroma minus Blue)\n";
+                                                        break;
+                                                case 3:
+                                                        $output_str .= "Cr (Chroma minus Red)\n";
+                                                        break;
+                                                case 4:
+                                                        $output_str .= "Red\n";
+                                                        break;
+                                                case 5:
+                                                        $output_str .= "Green\n";
+                                                        break;
+                                                case 6:
+                                                        $output_str .= "Blue\n";
+                                                        break;
+                                                default:
+                                                        $output_str .= "Unknown value $value\n";
+                                        };
+                                }
+
+                                // Return the completed string
+
+                                return $output_str;
+                                break;
+
+
+
+                        case 41730: // Colour Filter Array Pattern
+
+                                // The first two characters are a SHORT for Horizontal repeat pixel unit -
+                                $n_max = get_IFD_Data_Type( substr( $Tag['Data'], 0, 2 ), 3, $Tag['Byte Align'] );
+
+                                // The next two characters are a SHORT for Vertical repeat pixel unit -
+                                $m_max = get_IFD_Data_Type( substr( $Tag['Data'], 2, 2 ), 3, $Tag['Byte Align'] );
+
+
+                                // At least one camera type appears to have byte reversed values for N_Max and M_Max
+                                // Check if they need reversing
+                                if ( $n_max > 256 )
+                                {
+                                        $n_max = $n_max/256 + 256*($n_max%256);
+                                }
+
+                                if ( $m_max > 256 )
+                                {
+                                        $m_max = $m_max/256 + 256*($m_max%256);
+                                }
+
+
+                                $output_str = "";
+
+
+                                // Cycle through all the elements in the resulting 2 dimensional array,
+                                for( $m = 1; $m <= $m_max; $m++ )
+                                {
+                                        for( $n = 1; $n <= $n_max; $n++ )
+                                        {
+
+                                                // Append text from a lookup table according to
+                                                // the value read for this element
+
+                                                switch ( ord($Tag['Data']{($n_max*($m-1)+$n+3)}) )
+                                                {
+                                                        case 0:
+                                                                $output_str .= "RED     ";
+                                                                break;
+                                                        case 1:
+                                                                $output_str .= "GREEN   ";
+                                                                break;
+                                                        case 2:
+                                                                $output_str .= "BLUE    ";
+                                                                break;
+                                                        case 3:
+                                                                $output_str .= "CYAN    ";
+                                                                break;
+                                                        case 4:
+                                                                $output_str .= "MAGENTA ";
+                                                                break;
+                                                        case 5:
+                                                                $output_str .= "YELLOW  ";
+                                                                break;
+                                                        case 6:
+                                                                $output_str .= "WHITE   ";
+                                                                break;
+                                                        default:
+                                                                $output_str .= "Unknown ";
+                                                                break;
+                                                };
+                                        };
+                                        $output_str .= "\n";
+                                };
+
+                                // Return the resulting string
+                                return $output_str;
+                                break;
+
+                        default:
+                                return FALSE;
+                }
+        }
+        else
+        {
+                // Unknown IFD type, see if it is part of a makernote
+                return get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name );
+        }
+
+
+}
+
+/******************************************************************************
+* End of Function:     get_Tag_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     interpret_IFD
+*
+* Description:  Generates html detailing the contents a single IFD.
+*
+* Parameters:   IFD_array - the array containing an IFD
+*               filename - the name of the Image file being processed ( used
+*                          by scripts which displays EXIF thumbnails)
+*
+* Returns:      output_str - A string containing the HTML
+*
+******************************************************************************/
+
+function interpret_IFD( $IFD_array, $filename )
+{
+        // Create the output string with the table tag
+        $output_str = "<table class=\"EXIF_Table\" border=1>\n";
+
+        // Create an extra output string to receive any supplementary html
+        // which cannot go inside the table
+        $extra_IFD_str = "";
+
+        // Check that the IFD array is valid
+        if ( ( $IFD_array === FALSE ) || ( $IFD_array === NULL ) )
+        {
+                // the IFD array is NOT valid - exit
+                return "";
+        }
+
+        // Check if this is an EXIF IFD and if there is a makernote present
+        if ( ( $IFD_array['Tags Name'] === "EXIF" ) &&
+             ( ! array_key_exists( 37500, $IFD_array ) ) )
+        {
+
+                // This is an EXIF IFD but NO makernote is present - Add a message to the output
+                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">No Makernote Present</h3>";
+        }
+
+        // Cycle through each tag in the IFD
+
+        foreach( $IFD_array as $Tag_ID => $Exif_Tag )
+        {
+
+                // Ignore the non numeric elements - they aren't tags
+                if ( ! is_numeric ( $Tag_ID ) )
+                {
+                        // Skip Tags Name
+                }
+                        // Check if the Tag has been decoded successfully
+                else if ( $Exif_Tag['Decoded'] == TRUE )
+                {
+                        // This tag has been successfully decoded
+
+                        // Table cells won't get drawn with nothing in them -
+                        // Ensure that at least a non breaking space exists in them
+
+                        if ( trim($Exif_Tag['Text Value']) == "" )
+                        {
+                                $Exif_Tag['Text Value'] = "&nbsp;";
+                        }
+
+                        // Check if the tag is a sub-IFD
+                        if ( $Exif_Tag['Type'] == "SubIFD" )
+                        {
+                                // This is a sub-IFD tag
+                                // Add a sub-heading for the sub-IFD
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">" . $Exif_Tag['Tag Name'] . " contents</h3>";
+
+                                // Cycle through each sub-IFD in the chain
+                                foreach ( $Exif_Tag['Data'] as $subIFD )
+                                {
+                                        // Interpret this sub-IFD and add the html to the secondary output
+                                        $extra_IFD_str .= interpret_IFD( $subIFD, $filename );
+                                }
+                        }
+                                // Check if the tag is a makernote
+                        else if ( $Exif_Tag['Type'] == "Maker Note" )
+                        {
+                                // This is a Makernote Tag
+                                // Add a sub-heading for the Makernote
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Maker Note Contents</h3>";
+
+                                // Interpret the Makernote and add the html to the secondary output
+                                $extra_IFD_str .= Interpret_Makernote_to_HTML( $Exif_Tag, $filename );
+                        }
+                                // Check if this is a IPTC/NAA Record within the EXIF IFD
+                        else if ( $Exif_Tag['Type'] == "IPTC" )
+                        {
+                                // This is a IPTC/NAA Record, interpret it and output to the secondary html
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains IPTC/NAA Embedded in EXIF</h3>";
+                                $extra_IFD_str .=Interpret_IPTC_to_HTML( $Exif_Tag['Data'] );
+                        }
+                                // Change: Check for embedded XMP as of version 1.11
+                                // Check if this is a XMP Record within the EXIF IFD
+                        else if ( $Exif_Tag['Type'] == "XMP" )
+                        {
+                                // This is a XMP Record, interpret it and output to the secondary html
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains XMP Embedded in EXIF</h3>";
+                                $extra_IFD_str .= Interpret_XMP_to_HTML( $Exif_Tag['Data'] );
+                        }
+                                // Change: Check for embedded IRB as of version 1.11
+                                // Check if this is a Photoshop IRB Record within the EXIF IFD
+                        else if ( $Exif_Tag['Type'] == "IRB" )
+                        {
+                                // This is a Photoshop IRB Record, interpret it and output to the secondary html
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Contains Photoshop IRB Embedded in EXIF</h3>";
+                                $extra_IFD_str .= Interpret_IRB_to_HTML( $Exif_Tag['Data'], $filename );
+                        }
+                                // Check if the tag is Numeric
+                        else if ( $Exif_Tag['Type'] == "Numeric" )
+                        {
+                                // Numeric Tag - Output text value as is.
+                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . $Exif_Tag['Text Value'] . "</td></tr>\n";
+                        }
+                        else
+                        {
+                                // Other tag - Output text as preformatted
+                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value']) . "</pre></td></tr>\n";
+                        }
+
+                }
+                else
+                {
+                        // Tag has NOT been decoded successfully
+                        // Hence it is either an unknown tag, or one which
+                        // requires processing at the time of html construction
+
+                        // Table cells won't get drawn with nothing in them -
+                        // Ensure that at least a non breaking space exists in them
+
+                        if ( trim($Exif_Tag['Text Value']) == "" )
+                        {
+                                $Exif_Tag['Text Value'] = "&nbsp;";
+                        }
+
+                        // Check if this tag is the first IFD Thumbnail
+                        if ( ( $IFD_array['Tags Name'] == "TIFF" ) &&
+                             ( $Tag_ID == 513 ) )
+                        {
+                                // This is the first IFD thumbnail - Add html to the output
+
+                                // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                                // Build the path of the thumbnail script and its filename parameter to put in a url
+                                $link_str = get_relative_path( dirname(__FILE__) . "/get_exif_thumb.php" , getcwd ( ) );
+                                $link_str .= "?filename=";
+                                $link_str .= get_relative_path( $filename, dirname(__FILE__) );
+
+                                // Add thumbnail link to html
+                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><a class=\"EXIF_First_IFD_Thumb_Link\" href=\"$link_str\"><img class=\"EXIF_First_IFD_Thumb\" src=\"$link_str\"></a></td></tr>\n";
+                        }
+                                // Check if this is the Makernote
+                        else if ( $Exif_Tag['Type'] == "Maker Note" )
+                        {
+                                // This is the makernote, but has not been decoded
+                                // Add a message to the secondary output
+                                $extra_IFD_str .= "<h3 class=\"EXIF_Secondary_Heading\">Makernote Coding Unknown</h3>\n";
+                        }
+                        else
+                        {
+                                // This is an Unknown Tag
+
+                                // Check if the user wants to hide unknown tags
+                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] === FALSE )
+                                {
+                                        // User wants to display unknown tags
+
+                                        // Check if the Data is an ascii string
+                                        if ( $Exif_Tag['Data Type'] == 2 )
+                                        {
+                                                // This is a Ascii String field - add it preformatted to the output
+                                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\"><pre>" . trim( $Exif_Tag['Text Value'] ) . "</pre></td></tr>\n";
+                                        }
+                                        else
+                                        {
+                                                // Not an ASCII string - add it as is to the output
+                                                $output_str .= "<tr class=\"EXIF_Table_Row\"><td class=\"EXIF_Caption_Cell\">" . $Exif_Tag['Tag Name'] . "</td><td class=\"EXIF_Value_Cell\">" . trim( $Exif_Tag['Text Value'] ) . "</td></tr>\n";
+                                        }
+                                }
+                        }
+                }
+        }
+
+        // Close the table in the output
+        $output_str .= "</table>\n";
+
+        // Add the secondary output at the end of the main output
+        $output_str .= "$extra_IFD_str\n";
+
+        // Return the resulting html
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     interpret_IFD
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_IFD_Data_Type
+*
+* Description:  Decodes an IFD field value from a binary data string, using
+*               information supplied about the data type and byte alignment of
+*               the stored data.
+*               This function should be used for all datatypes except ASCII strings
+*
+* Parameters:   input_data - a binary data string containing the IFD value,
+*                            must be exact length of the value
+*               data_type - a number representing the IFD datatype as per the
+*                           TIFF 6.0 specification:
+*                               1 = Unsigned 8-bit Byte
+*                               2 = ASCII String
+*                               3 = Unsigned 16-bit Short
+*                               4 = Unsigned 32-bit Long
+*                               5 = Unsigned 2x32-bit Rational
+*                               6 = Signed 8-bit Byte
+*                               7 = Undefined
+*                               8 = Signed 16-bit Short
+*                               9 = Signed 32-bit Long
+*                               10 = Signed 2x32-bit Rational
+*                               11 = 32-bit Float
+*                               12 = 64-bit Double
+*               Byte_Align - Indicates the byte alignment of the data.
+*                            MM = Motorola, MSB first, Big Endian
+*                            II = Intel, LSB first, Little Endian
+*
+* Returns:      output - the value of the data (string or numeric)
+*
+******************************************************************************/
+
+function get_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
+{
+        // Check if this is a Unsigned Byte, Unsigned Short or Unsigned Long
+        if (( $data_type == 1 ) || ( $data_type == 3 ) || ( $data_type == 4 ))
+        {
+                // This is a Unsigned Byte, Unsigned Short or Unsigned Long
+
+                // Check the byte alignment to see if the bytes need tp be reversed
+                if ( $Byte_Align == "II" )
+                {
+                        // This is in Intel format, reverse it
+                        $input_data = strrev ( $input_data );
+                }
+
+                // Convert the binary string to a number and return it
+                return hexdec( bin2hex( $input_data ) );
+        }
+                // Check if this is a ASCII string type
+        elseif ( $data_type == 2 )
+        {
+                // Null terminated ASCII string(s)
+                // The input data may represent multiple strings, as the
+                // 'count' field represents the total bytes, not the number of strings
+                // Hence this should not be processed here, as it would have
+                // to return multiple values instead of a single value
+
+                echo "<p>Error - ASCII Strings should not be processed in get_IFD_Data_Type</p>\n";
+                return "Error Should never get here"; //explode( "\x00", $input_data );
+        }
+                // Check if this is a Unsigned rational type
+        elseif ( $data_type == 5 )
+        {
+                // This is a Unsigned rational type
+
+                // Check the byte alignment to see if the bytes need to be reversed
+                if ( $Byte_Align == "MM" )
+                {
+                        // Motorola MSB first byte aligment
+                        // Unpack the Numerator and denominator and return them
+                        return unpack( 'NNumerator/NDenominator', $input_data );
+                }
+                else
+                {
+                        // Intel LSB first byte aligment
+                        // Unpack the Numerator and denominator and return them
+                        return unpack( 'VNumerator/VDenominator', $input_data );
+                }
+        }
+                // Check if this is a Signed Byte, Signed Short or Signed Long
+        elseif ( ( $data_type == 6 ) || ( $data_type == 8 ) || ( $data_type == 9 ) )
+        {
+                // This is a Signed Byte, Signed Short or Signed Long
+
+                // Check the byte alignment to see if the bytes need to be reversed
+                if ( $Byte_Align == "II" )
+                {
+                        //Intel format, reverse the bytes
+                        $input_data = strrev ( $input_data );
+                }
+
+                // Convert the binary string to an Unsigned number
+                $value = hexdec( bin2hex( $input_data ) );
+
+                // Convert to signed number
+
+                // Check if it is a Byte above 128 (i.e. a negative number)
+                if ( ( $data_type == 6 ) && ( $value > 128 ) )
+                {
+                        // number should be negative - make it negative
+                        return  $value - 256;
+                }
+
+                // Check if it is a Short above 32767 (i.e. a negative number)
+                if ( ( $data_type == 8 ) && ( $value > 32767 ) )
+                {
+                        // number should be negative - make it negative
+                        return  $value - 65536;
+                }
+
+                // Check if it is a Long above 2147483648 (i.e. a negative number)
+                if ( ( $data_type == 9 ) && ( $value > 2147483648 ) )
+                {
+                        // number should be negative - make it negative
+                        return  $value - 4294967296;
+                }
+
+                // Return the signed number
+                return $value;
+        }
+                // Check if this is Undefined type
+        elseif ( $data_type == 7 )
+        {
+                // Custom Data - Do nothing
+                return $input_data;
+        }
+                // Check if this is a Signed Rational type
+        elseif ( $data_type == 10 )
+        {
+                // This is a Signed Rational type
+
+                // Signed Long not available with endian in unpack , use unsigned and convert
+
+                // Check the byte alignment to see if the bytes need to be reversed
+                if ( $Byte_Align == "MM" )
+                {
+                        // Motorola MSB first byte aligment
+                        // Unpack the Numerator and denominator
+                        $value = unpack( 'NNumerator/NDenominator', $input_data );
+                }
+                else
+                {
+                        // Intel LSB first byte aligment
+                        // Unpack the Numerator and denominator
+                        $value = unpack( 'VNumerator/VDenominator', $input_data );
+                }
+
+                // Convert the numerator to a signed number
+                // Check if it is above 2147483648 (i.e. a negative number)
+                if ( $value['Numerator'] > 2147483648 )
+                {
+                        // number is negative
+                        $value['Numerator'] -= 4294967296;
+                }
+
+                // Convert the denominator to a signed number
+                // Check if it is above 2147483648 (i.e. a negative number)
+                if ( $value['Denominator'] > 2147483648 )
+                {
+                        // number is negative
+                        $value['Denominator'] -= 4294967296;
+                }
+
+                // Return the Signed Rational value
+                return $value;
+        }
+                // Check if this is a Float type
+        elseif ( $data_type == 11 )
+        {
+                // IEEE 754 Float
+                // TODO - EXIF - IFD datatype Float not implemented yet
+                return "FLOAT NOT IMPLEMENTED YET";
+        }
+                // Check if this is a Double type
+        elseif ( $data_type == 12 )
+        {
+                // IEEE 754 Double
+                // TODO - EXIF - IFD datatype Double not implemented yet
+                return "DOUBLE NOT IMPLEMENTED YET";
+        }
+        else
+        {
+                // Error - Invalid Datatype
+                return "Invalid Datatype $data_type";
+
+        }
+
+}
+
+/******************************************************************************
+* End of Function:     get_IFD_Data_Type
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_IFD_Data_Type
+*
+* Description:  Encodes an IFD field from a value to a binary data string, using
+*               information supplied about the data type and byte alignment of
+*               the stored data.
+*
+* Parameters:   input_data - an IFD data value, numeric or string
+*               data_type - a number representing the IFD datatype as per the
+*                           TIFF 6.0 specification:
+*                               1 = Unsigned 8-bit Byte
+*                               2 = ASCII String
+*                               3 = Unsigned 16-bit Short
+*                               4 = Unsigned 32-bit Long
+*                               5 = Unsigned 2x32-bit Rational
+*                               6 = Signed 8-bit Byte
+*                               7 = Undefined
+*                               8 = Signed 16-bit Short
+*                               9 = Signed 32-bit Long
+*                               10 = Signed 2x32-bit Rational
+*                               11 = 32-bit Float
+*                               12 = 64-bit Double
+*               Byte_Align - Indicates the byte alignment of the data.
+*                            MM = Motorola, MSB first, Big Endian
+*                            II = Intel, LSB first, Little Endian
+*
+* Returns:      output - the packed binary string of the data
+*
+******************************************************************************/
+
+function put_IFD_Data_Type( $input_data, $data_type, $Byte_Align )
+{
+        // Process according to the datatype
+        switch ( $data_type )
+        {
+                case 1: // Unsigned Byte - return character as is
+                        return chr($input_data);
+                        break;
+
+                case 2: // ASCII String
+                        // Return the string with terminating null
+                        return $input_data . "\x00";
+                        break;
+
+                case 3: // Unsigned Short
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the short and return
+                                return pack( "v", $input_data );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the short and return
+                                return pack( "n", $input_data );
+                        }
+                        break;
+
+                case 4: // Unsigned Long
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the long and return
+                                return pack( "V", $input_data );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the long and return
+                                return pack( "N", $input_data );
+                        }
+                        break;
+
+                case 5: // Unsigned Rational
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the two longs and return
+                                return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the two longs and return
+                                return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
+                        }
+                        break;
+
+                case 6: // Signed Byte
+                        // Check if number is negative
+                        if ( $input_data < 0 )
+                        {
+                                // Number is negative - return signed character
+                                return chr( $input_data + 256 );
+                        }
+                        else
+                        {
+                                // Number is positive - return character
+                                return chr( $input_data );
+                        }
+                        break;
+
+                case 7: // Unknown - return as is
+                        return $input_data;
+                        break;
+
+                case 8: // Signed Short
+                        // Check if number is negative
+                        if (  $input_data < 0 )
+                        {
+                                // Number is negative - make signed value
+                                $input_data = $input_data + 65536;
+                        }
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the short and return
+                                return pack( "v", $input_data );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the short and return
+                                return pack( "n", $input_data );
+                        }
+                        break;
+
+                case 9: // Signed Long
+                        // Check if number is negative
+                        if (  $input_data < 0 )
+                        {
+                                // Number is negative - make signed value
+                                $input_data = $input_data + 4294967296;
+                        }
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the long and return
+                                return pack( "v", $input_data );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the long and return
+                                return pack( "n", $input_data );
+                        }
+                        break;
+
+                case 10: // Signed Rational
+                        // Check if numerator is negative
+                        if (  $input_data['Numerator'] < 0 )
+                        {
+                                // Number is numerator - make signed value
+                                $input_data['Numerator'] = $input_data['Numerator'] + 4294967296;
+                        }
+                        // Check if denominator is negative
+                        if (  $input_data['Denominator'] < 0 )
+                        {
+                                // Number is denominator - make signed value
+                                $input_data['Denominator'] = $input_data['Denominator'] + 4294967296;
+                        }
+                        // Check byte alignment
+                        if ( $Byte_Align == "II" )
+                        {
+                                // Intel/Little Endian - pack the two longs and return
+                                return pack( "VV", $input_data['Numerator'], $input_data['Denominator'] );
+                        }
+                        else
+                        {
+                                // Motorola/Big Endian - pack the two longs and return
+                                return pack( "NN", $input_data['Numerator'], $input_data['Denominator'] );
+                        }
+                        break;
+
+                case 11: // Float
+                        // IEEE 754 Float
+                        // TODO - EXIF - IFD datatype Float not implemented yet
+                        return "FLOAT NOT IMPLEMENTED YET";
+                        break;
+
+                case 12: // Double
+                        // IEEE 754 Double
+                        // TODO - EXIF - IFD datatype Double not implemented yet
+                        return "DOUBLE NOT IMPLEMENTED YET";
+                        break;
+
+                default:
+                        // Error - Invalid Datatype
+                        return "Invalid Datatype $data_type";
+                        break;
+
+        }
+
+        // Shouldn't get here
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     put_IFD_Data_Type
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_IFD_value_as_text
+*
+* Description:  Decodes an IFD field value from a binary data string, using
+*               information supplied about the data type and byte alignment of
+*               the stored data.
+*               This function should be used for all datatypes except ASCII strings
+*
+* Parameters:   input_data - a binary data string containing the IFD value,
+*                            must be exact length of the value
+*               data_type - a number representing the IFD datatype as per the
+*                           TIFF 6.0 specification:
+*                               1 = Unsigned 8-bit Byte
+*                               2 = ASCII String
+*                               3 = Unsigned 16-bit Short
+*                               4 = Unsigned 32-bit Long
+*                               5 = Unsigned 2x32-bit Rational
+*                               6 = Signed 8-bit Byte
+*                               7 = Undefined
+*                               8 = Signed 16-bit Short
+*                               9 = Signed 32-bit Long
+*                               10 = Signed 2x32-bit Rational
+*                               11 = 32-bit Float
+*                               12 = 64-bit Double
+*               Byte_Align - Indicates the byte alignment of the data.
+*                            MM = Motorola, MSB first, Big Endian
+*                            II = Intel, LSB first, Little Endian
+*
+* Returns:      output - the value of the data (string or numeric)
+*
+******************************************************************************/
+
+function get_IFD_value_as_text( $Exif_Tag )
+{
+        // Create a string to receive the output text
+        $output_str = "";
+
+        // Select Processing method according to the datatype
+        switch  ($Exif_Tag['Data Type'])
+        {
+                case 1 : // Unsigned Byte
+                case 3 : // Unsigned Short
+                case 4 : // Unsigned Long
+                case 6 : // Signed Byte
+                case 8 : // Signed Short
+                case 9 : // Signed Long
+
+                        // Cycle through each of the values for this tag
+                        foreach ( $Exif_Tag['Data'] as $val )
+                        {
+                                // Check that this isn't the first value,
+                                if ( $output_str != "" )
+                                {
+                                        // This isn't the first value, Add a Comma and Newline to the output
+                                        $output_str .= ",\n";
+                                }
+                                // Add the Value to the output
+                                $output_str .= $val;
+                        }
+                        break;
+
+                case 2 : // ASCII
+                        // Append all the strings together, separated by Newlines
+                        $output_str .= implode ( "\n", $Exif_Tag['Data']);
+                        break;
+
+                case 5 : // Unsigned Rational
+                case 10: // Signed Rational
+
+                        // Cycle through each of the values for this tag
+                        foreach ( $Exif_Tag['Data'] as $val )
+                        {
+                                // Check that this isn't the first value,
+                                if ( $output_str != "" )
+                                {
+                                        // This isn't the first value, Add a Comma and Newline to the output
+                                        $output_str .= ",\n";
+                                }
+
+                                // Add the Full Value to the output
+                                $output_str .= $val['Numerator'] ."/" . $val['Denominator'];
+
+                                // Check if division by zero might be a problem
+                                if ( $val['Denominator'] != 0 )
+                                {
+                                        // Denominator is not zero, Add the Decimal Value to the output text
+                                        $output_str .= " (" . ($val['Numerator'] / $val['Denominator']) . ")";
+                                }
+                        }
+                        break;
+
+                case 11: // Float
+                case 12: // Double
+                        // TODO - EXIF - IFD datatype Double and Float not implemented yet
+                        $output_str .= "Float and Double not implemented yet";
+                        break;
+
+                case 7 : // Undefined
+                        // Unless the User has asked to see the raw binary data, this
+                        // type should not be displayed
+
+                        // Check if the user has requested to see the binary data in hex
+                        if ( $GLOBALS['SHOW_BINARY_DATA_HEX'] == TRUE)
+                        {
+                                // User has requested to see the binary data in hex
+                                // Add the value in hex
+                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . bin2hex( $Exif_Tag['Data'] )  ;
+                        }
+                                // Check if the user has requested to see the binary data as is
+                        else if ( $GLOBALS['SHOW_BINARY_DATA_TEXT'] == TRUE)
+                        {
+                                // User has requested to see the binary data as is
+                                // Add the value as is
+                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ): " . $Exif_Tag['Data']  ;
+                        }
+                        else
+                        {
+                                // User has NOT requested to see binary data,
+                                // Add a message indicating the number of bytes to the output
+                                $output_str .= "( " . strlen( $Exif_Tag['Data'] ) . " bytes of binary data ) "  ;
+                        }
+                        break;
+
+                default :
+                        // Error - Unknown IFD datatype
+                        $output_str .= "Error - Exif tag data type (" . $Exif_Tag['Data Type'] .") is invalid";
+                        break;
+        }
+
+        // Return the resulting text string
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     get_IFD_value_as_text
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+* Global Variable:      IFD_Data_Sizes
+*
+* Contents:     The sizes (in bytes) of each EXIF IFD Datatype, indexed by
+*               their datatype number
+*
+******************************************************************************/
+
+$GLOBALS['IFD_Data_Sizes'] = array(     1 => 1,         // Unsigned Byte
+                                        2 => 1,         // ASCII String
+                                        3 => 2,         // Unsigned Short
+                                        4 => 4,         // Unsigned Long
+                                        5 => 8,         // Unsigned Rational
+                                        6 => 1,         // Signed Byte
+                                        7 => 1,         // Undefined
+                                        8 => 2,         // Signed Short
+                                        9 => 4,         // Signed Long
+                                        10 => 8,        // Signed Rational
+                                        11 => 4,        // Float
+                                        12 => 8 );      // Double
+
+/******************************************************************************
+* End of Global Variable:     IFD_Data_Sizes
+******************************************************************************/
+
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/get_ps_thumb.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/get_ps_thumb.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/get_ps_thumb.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/get_ps_thumb.php	2004/11/25 12:55:52
@@ -0,0 +1,176 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     get_ps_thumb.php
+*
+* Description:  This script extracts a Photoshop IRB (Image Resource Block)
+*               thumbnail from within a JPEG file and allows it to be displayed
+*
+* Usage:        get_ps_thumb?filename=<filename>
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+*               1.00 -> 1.11 : Added support for Photoshop IRB thumbnails which are
+*                              embedded within EXIF information (used in TIFF files)
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+        // Ensure that nothing can write to the standard io, before we get the header out
+        ob_start( );
+
+
+        // retrieve the filename from the URL parameters
+
+        $filename = $GLOBALS['HTTP_GET_VARS']['filename'];
+
+        // Change: Check for file extension rather than assuming JPEG as of 1.11
+        // Retrieve the Filename Extension
+        $path_parts = pathinfo( $filename );
+
+        // Check if the Extension is JPEG
+        if ( ( strcasecmp( $path_parts["extension"], "jpg" ) == 0 ) ||
+             ( strcasecmp( $path_parts["extension"], "jpeg" ) == 0 ) )
+        {
+                // JPEG Extension
+
+                include 'JPEG.php';
+                include 'Photoshop_IRB.php';
+
+                // Retrieve the JPEG header Data
+
+                $jpeg_header_data = get_jpeg_header_data( $filename );
+
+                // Retrieve any Photoshop IRB data in the file
+
+                $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
+
+                // Check if Photoshop IRB data was retrieved
+
+                if ( $IRB_array === FALSE )
+                {
+                        // No Photoshop IRB data could be retrieved - abort
+                        ob_end_clean ( );
+                        echo "<p>Photoshop IRB could not be retrieved from the JPEG file</p>\n";
+                        return;
+                }
+
+                // Cycle through the resources in the Photoshop IRB
+                // Until either a thumbnail resource is found or
+                // there are no more resources
+                $i = 0;
+                while ( ( $i < count( $IRB_array ) ) &&
+                        ( $IRB_array[$i]['ResID'] != 0x0409 ) &&
+                        ( $IRB_array[$i]['ResID'] != 0x040C ) )
+                {
+                        $i++;
+                }
+
+
+                // Check if a thumbnail was found
+                if ( $i < count( $IRB_array ) )
+                {
+                        // A thumbnail was found, Display it
+                        ob_end_clean ( );
+                        header("Content-type: image/jpeg");
+                        print substr( $IRB_array[$i]['ResData'] , 28 );
+                }
+        }
+        // Change: Add support for TIFF Photoshop IRB thumbnails as of 1.11
+        // Check if file has TIFF extension
+        else if ( ( strcasecmp( $path_parts["extension"], "tif" ) == 0 ) ||
+                  ( strcasecmp( $path_parts["extension"], "tiff" ) == 0 ) )
+        {
+                // TIFF Extension
+
+                include 'EXIF.php';
+
+                // Retrieve the EXIF info
+                $exif_array = get_EXIF_TIFF( $filename );
+
+                // Retrieve any Photoshop IRB data in the EXIF
+                if ( ( array_key_exists( 0, $exif_array ) ) &&
+                     ( array_key_exists( 34377, $exif_array[0] ) ) &&
+                     ( array_key_exists( 'Data', $exif_array[0][34377] ) ) )
+                {
+                        $IRB_array = $exif_array[0][34377]['Data'];
+
+                        // Check if Photoshop IRB data was retrieved
+
+                        if ( $IRB_array === FALSE )
+                        {
+                                // No Photoshop IRB data could be retrieved - abort
+                                ob_end_clean ( );
+                                echo "<p>Photoshop IRB could not be retrieved from the TIFF file</p>\n";
+                                return;
+                        }
+
+                        // Cycle through the resources in the Photoshop IRB
+                        // Until either a thumbnail resource is found or
+                        // there are no more resources
+                        $i = 0;
+                        while ( ( $i < count( $IRB_array ) ) &&
+                                ( $IRB_array[$i]['ResID'] != 0x0409 ) &&
+                                ( $IRB_array[$i]['ResID'] != 0x040C ) )
+                        {
+                                $i++;
+                        }
+
+
+                        // Check if a thumbnail was found
+                        if ( $i < count( $IRB_array ) )
+                        {
+                                // A thumbnail was found, Display it
+                                ob_end_clean ( );
+                                header("Content-type: image/jpeg");
+                                print substr( $IRB_array[$i]['ResData'] , 28 );
+                        }
+                }
+                else
+                {
+                        // Embedded Photoshop IRB block not found
+                        ob_end_clean ( );
+                        echo "No Photoshop IRB found within EXIF";
+                }
+        }
+        else
+        {
+                // Unknown extension
+                ob_end_clean ( );
+                echo "Unknown file Type";
+        }
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/EXIF_Makernote.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Makernote.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Makernote.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/EXIF_Makernote.php	2005/01/20 10:47:22
@@ -0,0 +1,334 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     EXIF_Makernote.php
+*
+* Description:  Provides functions for reading EXIF Makernote Information
+*               The actual functions for reading each manufacturers makernote
+*               are provided in the Makernotes directory.
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.11 : changed makernotes directory definition to allow
+*                              the toolkit to be portable across directories
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// Create the Makernote Parser and Interpreter Function Array
+
+$GLOBALS['Makernote_Function_Array'] = array(   "Read_Makernote_Tag" => array( ),
+                                                "get_Makernote_Text_Value" => array( ),
+                                                "Interpret_Makernote_to_HTML" => array( ) );
+
+
+// Include the Main TIFF and EXIF Tags array
+
+include_once 'EXIF.php';
+
+
+
+/******************************************************************************
+*
+* Include the Makernote Scripts
+*
+******************************************************************************/
+
+// Set the Makernotes Directory
+
+$dir = dirname(__FILE__) . "/Makernotes/";      // Change: as of version 1.11 - to allow directory portability
+
+// Open the directory
+$dir_hnd = @opendir ( $dir );
+
+// Cycle through each of the files in the Makernotes directory
+
+while ( ( $file = readdir( $dir_hnd ) ) !== false )
+{
+        // Check if the current item is a file
+        if ( is_file ( $dir . $file ) )
+        {
+                // Item is a file, break it into it's parts
+                $path_parts = pathinfo( $dir . $file );
+
+                // Check if the extension is php
+                if ( $path_parts["extension"] == "php" )
+                {
+                        // This is a php script - include it
+                        include_once ($dir . $file) ;
+                }
+        }
+}
+// close the directory
+closedir( $dir_hnd );
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Read_Makernote_Tag
+*
+* Description:  Attempts to decodes the Makernote tag supplied, returning the
+*               new tag with the decoded information attached.
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               EXIF_Array - the entire EXIF array containing the
+*                            makernote, as returned from get_EXIF_JPEG, in
+*                            case more information is required for decoding
+*               filehnd - an open file handle for the file containing the
+*                         makernote - does not have to be positioned at the
+*                         start of the makernote
+*
+*
+* Returns:      Makernote_Tag - the Makernote_Tag from the parameters, but
+*                               modified to contain the decoded information
+*
+******************************************************************************/
+
+function Read_Makernote_Tag( $Makernote_Tag, $EXIF_Array, $filehnd )
+{
+
+        // Check if the Makernote is present but empty - this sometimes happens
+        if ( ( strlen( $Makernote_Tag['Data'] ) === 0 ) ||
+             ( $Makernote_Tag['Data'] === str_repeat ( "\x00", strlen( $Makernote_Tag['Data'] )) ) )
+        {
+                // Modify the makernote to display that it is empty
+                $Makernote_Tag['Decoded Data'] = "Empty";
+                $Makernote_Tag['Makernote Type'] = "Empty";
+                $Makernote_Tag['Makernote Tags'] = "Empty";
+                $Makernote_Tag['Decoded'] = TRUE;
+
+                // Return the new makernote
+                return $Makernote_Tag;
+        }
+
+        // Check if the Make Field exists in the TIFF IFD
+        if ( array_key_exists ( 271, $EXIF_Array[0] ) )
+        {
+                // A Make tag exists in IFD0, collapse multiple strings (if any), and save result
+                $Make_Field = implode ( "\n", $EXIF_Array[0][271]['Data']);
+        }
+        else
+        {
+                // No Make field found
+                $Make_Field = "";
+        }
+
+        // Cycle through each of the "Read_Makernote_Tag" functions
+
+        foreach( $GLOBALS['Makernote_Function_Array']['Read_Makernote_Tag'] as $func )
+        {
+                // Run the current function, and save the result
+                $New_Makernote_Tag = $func( $Makernote_Tag, $EXIF_Array, $filehnd, $Make_Field );
+
+                // Check if a valid result was returned
+                if ( $New_Makernote_Tag !== FALSE )
+                {
+                        // A valid result was returned - stop cycling
+                        break;
+                }
+        }
+
+        // Check if a valid result was returned
+        if ( $New_Makernote_Tag === false )
+        {
+                // A valid result was NOT returned - construct a makernote tag representing this
+                $New_Makernote_Tag = $Makernote_Tag;
+                $New_Makernote_Tag['Decoded'] = FALSE;
+                $New_Makernote_Tag['Makernote Type'] = "Unknown Makernote";
+        }
+
+        // Return the new makernote tag
+        return $New_Makernote_Tag;
+
+}
+
+/******************************************************************************
+* End of Function:     Read_Makernote_Tag
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Makernote_Text_Value
+*
+* Description:  Attempts to provide a text value for any makernote tag marked
+*               as type special. Returns false no handler could be found to
+*               process the tag
+*
+* Parameters:   Exif_Tag - the element of an the Makernote array containing the
+*                          tag in question, as returned from Read_Makernote_Tag
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+*
+* Returns:      output - the text value for the tag
+*               FALSE - If no handler could be found to process this tag, or if
+*                       an error occured in decoding
+*
+******************************************************************************/
+
+function get_Makernote_Text_Value( $Tag, $Tag_Definitions_Name )
+{
+
+        // Cycle through each of the "get_Makernote_Text_Value" functions
+
+        foreach( $GLOBALS['Makernote_Function_Array']['get_Makernote_Text_Value'] as $func )
+        {
+                // Run the current function, and save the result
+                $Text_Val = $func( $Tag, $Tag_Definitions_Name );
+
+                // Check if a valid result was returned
+                if ( $Text_Val !== FALSE )
+                {
+                        // valid result - return it
+                        return $Text_Val;
+                }
+        }
+
+        // No Special tag handler found for this tag - return false
+        return FALSE;
+
+}
+
+
+/******************************************************************************
+* End of Function:     get_Makernote_Text_Value
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_Makernote_to_HTML
+*
+* Description:  Attempts to interpret a makernote into html.
+*
+* Parameters:   Makernote_Tag - the element of an EXIF array containing the
+*                               makernote, as returned from get_EXIF_JPEG
+*               filename - the name of the JPEG file being processed ( used
+*                          by scripts which display embedded thumbnails)
+*
+*
+* Returns:      output - the html representing the makernote
+*
+******************************************************************************/
+
+function Interpret_Makernote_to_HTML( $Makernote_tag, $filename )
+{
+
+        // Create a string to receive the HTML
+        $output_str = "";
+
+        // Check if the makernote tag is valid
+        if ( $Makernote_tag === FALSE )
+        {
+                // No makernote info - return
+                return $output_str;
+        }
+
+
+        // Check if the makernote has been marked as unknown
+        if ( $Makernote_tag['Makernote Type'] == "Unknown Makernote" )
+        {
+                // Makernote is unknown - return message
+                $output_str .= "<h4 class=\"EXIF_Makernote_Small_Heading\">Unknown Makernote Coding</h4>\n";
+                return $output_str;
+        }
+        else
+        {
+                // Makernote is known - add a heading to the output
+                $output_str .= "<p class=\"EXIF_Makernote_Text\">Makernote Coding: " . $Makernote_tag['Makernote Type'] . "</p>\n";
+        }
+
+        // Check if this is an empty makernote
+        if ( $Makernote_tag['Makernote Type'] == "Empty" )
+        {
+                // It is empty - don't try to interpret
+                return $output_str;
+        }
+
+        // Cycle through each of the "Interpret_Makernote_to_HTML" functions
+
+        foreach( $GLOBALS['Makernote_Function_Array']['Interpret_Makernote_to_HTML'] as $func )
+        {
+                // Run the current function, and save the result
+                $html_text = $func( $Makernote_tag, $filename );
+
+                // Check if a valid result was returned
+                if ( $html_text !== FALSE )
+                {
+                        // valid result - return it
+                        return $output_str . $html_text;
+                }
+        }
+
+        // No Interpreter function handled the makernote - return a message
+
+        $output_str .= "<h4 class=\"EXIF_Makernote_Small_Heading\">Could not Decode Makernote, it may be corrupted or empty</h4>\n";
+
+        return $output_str;
+
+
+}
+
+/******************************************************************************
+* End of Function:     Interpret_Makernote_to_HTML
+******************************************************************************/
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/get_exif_thumb.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/get_exif_thumb.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/get_exif_thumb.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/get_exif_thumb.php	2004/08/05 16:09:04
@@ -0,0 +1,98 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     get_exif_thumb.php
+*
+* Description:  This script extracts a EXIF thumbnail from the first IFD of a
+*               JPEG file and allows it to be displayed
+*
+* Usage:        get_exif_thumb?filename=<filename>
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+        // Ensure that nothing can write to the standard io, before we get the header out
+        ob_start( );
+        
+        
+        include 'JPEG.php';
+        include 'EXIF.php';
+
+
+        // retrieve the filename from the URL parameters
+
+        $filename = $GLOBALS['HTTP_GET_VARS']['filename'];
+
+        // Retrieve any EXIF data in the file
+
+        $Exif_array = get_EXIF_JPEG( $filename );
+
+        // Check if EXIF data was retrieved
+
+        if ( $Exif_array === FALSE )
+        {
+                // No EXIF data could be retrieved - abort
+                ob_end_clean ( );
+                echo "<p>EXIF segment could not be retrieved</p>\n";
+                return;
+        }
+
+
+        // Check if the First IFD exists ( The First IFD is actually the second, since there is also the zeroth IFD)
+        if ( count( $Exif_array ) < 2  )
+        {
+                ob_end_clean ( );
+                echo "<p>Couldn't find Thumbnail IFD</p>\n";
+                return;
+        }
+
+        // Check if the First IFD contains the Thumbnail tag
+        if ( array_key_exists( 513, $Exif_array[1] ) )
+        {
+                // Output the thumbnail
+                ob_end_clean ( );
+                header("Content-type: image/jpeg");
+                print $Exif_array[1][513]['Data'];
+        }
+        else
+        {
+                ob_end_clean ( );
+                echo "<p>Couldn't find Thumbnail Tag</p>\n";
+                return;
+        }
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/XMP.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/XMP.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/XMP.php	2011/08/17 20:09:02	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/XMP.php	2004/11/22 21:26:54
@@ -0,0 +1,1063 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     XMP.php
+*
+* Description:  Provides functions for reading and writing information to/from
+*               the 'App 1' Extensible Metadata Platform (XMP) segment of JPEG
+*               format files. This XMP segment is XML based and contains the
+*               Resource Description Framework (RDF) data, which itself can
+*               contain the Dublin Core Metadata Initiative (DCMI) information.
+*
+* Author:       Evan Hunter
+*
+* Date:         27/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.10
+*
+* Changes:      1.00 -> 1.04 : changed put_IPTC to fix a bug preventing the correct
+*               insertion of a XMP block where none existed previously
+*
+*               1.04 -> 1.10 : changed put_XMP_text to fix some array indexes which were missing qoutes
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+include_once 'XML.php';
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_XMP_text
+*
+* Description:  Retrieves the Extensible Metadata Platform (XMP) information
+*               from an App1 JPEG segment and returns the raw XML text as a
+*               string. This includes the Resource Description Framework (RDF)
+*               information and may include Dublin Core Metadata Initiative (DCMI)
+*               information. Uses information supplied by the get_jpeg_header_data
+*               function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      xmp_data - the string of raw XML text
+*               FALSE - if an APP 1 XMP segment could not be found,
+*                       or if an error occured
+*
+******************************************************************************/
+
+function get_XMP_text( $jpeg_header_data )
+{
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP1 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
+                {
+                        // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 )
+                        {
+                                // Found a XMP/RDF block
+                                // Return the XMP text
+                                $xmp_data = substr ( $jpeg_header_data[$i]['SegData'], 29 );
+
+                                return $xmp_data;
+                        }
+                }
+        }
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_XMP_text
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_XMP_text
+*
+* Description:  Adds or modifies the Extensible Metadata Platform (XMP) information
+*               in an App1 JPEG segment. If a XMP segment already exists, it is
+*               replaced, otherwise a new one is inserted, using the supplied data.
+*               Uses information supplied by the get_jpeg_header_data function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*               newXMP - a string containing the XMP text to be stored in the XMP
+*                        segment. Should be constructed using the write_XMP_array_to_text
+*                        function
+*
+* Returns:      jpeg_header_data - the JPEG header data array with the
+*                                  XMP segment added.
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function put_XMP_text( $jpeg_header_data, $newXMP )
+{
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP1 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP1" ) == 0 )
+                {
+                        // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0 )
+                        {
+                                // Found a preexisting XMP/RDF block - Replace it with the new one and return.
+                                $jpeg_header_data[$i]['SegData'] = "http://ns.adobe.com/xap/1.0/\x00" . $newXMP;
+                                return $jpeg_header_data;
+                        }
+                }
+        }
+
+        // No pre-existing XMP/RDF found - insert a new one after any pre-existing APP0 or APP1 blocks
+        // Change: changed to initialise $i properly as of revision 1.04
+        $i = 0;
+        // Loop until a block is found that isn't an APP0 or APP1
+        while ( ( $jpeg_header_data[$i]['SegName'] == "APP0" ) || ( $jpeg_header_data[$i]['SegName'] == "APP1" ) )
+        {
+                $i++;
+        }
+
+
+
+        // Insert a new XMP/RDF APP1 segment at the specified point.
+        // Change: changed to properly construct array element as of revision 1.04 - requires two array statements not one, requires insertion at $i, not $i - 1
+        array_splice($jpeg_header_data, $i, 0, array( array(       "SegType" => 0xE1,
+                                                                        "SegName" => "APP1",
+                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE1 ],
+                                                                        "SegData" => "http://ns.adobe.com/xap/1.0/\x00" . $newXMP ) ) );
+
+        // Return the headers with the new segment inserted
+        return $jpeg_header_data;
+}
+
+/******************************************************************************
+* End of Function:     put_XMP_text
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     read_XMP_array_from_text
+*
+* Description:  An alias for read_xml_array_from_text.
+*               Parses a string containing XMP data (XML), and returns the resulting
+*               tree structure array, which contains all the XMP (XML) information.
+*               Note: White space and comments in the XMP data (XML) are ignored
+*               Note: All text information contained in the tree structure
+*                     is encoded as Unicode UTF-8. Hence text will appear as
+*                     normal ASCII except where there is an extended character.
+*
+* Parameters:   xmptext - a string containing the XMP data (XML) to be parsed
+*
+* Returns:      output - the tree structure array containing the XMP (XML) information
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function read_XMP_array_from_text( $xmptext )
+{
+        return read_xml_array_from_text( $xmptext );
+}
+
+/******************************************************************************
+* End of Function:     read_XMP_array_from_text
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     write_XMP_array_to_text
+*
+* Description:  Takes a tree structure array containing XMP (in the same format
+*               as returned by read_XMP_array_from_text, and constructs a string
+*               containing the equivalent XMP, including the XMP Packet header
+*               and trailer. Produces XMP text which has correct indents, encoded
+*               using UTF-8.
+*               Note: All text information contained in the tree structure
+*                     can be either 7-bit ASCII or encoded as Unicode UTF-8,
+*                     since UTF-8 passes 7-bit ASCII text unchanged.
+*
+* Parameters:   xmparray - the tree structure array containing the information to
+*                          be converted to XMP text
+*
+* Returns:      output_XMP_text - the string containing the equivalent XMP text
+*
+******************************************************************************/
+
+function write_XMP_array_to_text( $xmparray )
+{
+        // Add the XMP packet header
+        // The sequence 0xEFBBBF is the UTF-8 encoded version of the Unicode “zero
+        // width non-breaking space character” (U+FEFF), which is used for detecting
+        // whether UTF-16 or UTF-8 is being used.
+        $output_XMP_text = "<?xpacket begin='\xef\xbb\xbf' id='W5M0MpCehiHzreSzNTczkc9d'?>\n";
+
+        // Photoshop Seems to add this, but there doesn't appear to be
+        // any information on what it means
+        // TODO : XMP, Find out what the adobe-xap-filters tag means
+        $output_XMP_text .= "<?adobe-xap-filters esc=\"CR\"?>\n";
+
+        // Add the XML text
+        $output_XMP_text .= write_xml_array_to_text( $xmparray, 0 );
+
+
+        // The XMP standard recommends adding 2-4k of white space at the
+        // end for in place editing, so we will add it to the XML now
+        $output_XMP_text .= str_repeat("                                                                                                   \n", 30);
+
+        // Add the XMP packet trailer
+        $output_XMP_text .= "<?xpacket end='w'?>";
+
+        // Return the resulting XMP text
+        return $output_XMP_text;
+}
+
+/******************************************************************************
+* End of Function:     write_XMP_array_to_text
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_XMP_to_HTML
+*
+* Description:  Generates html showing the information contained in an Extensible
+*               Metadata Platform (XMP) tree structure array, as retrieved
+*               with read_XMP_array_from_text
+*
+* Parameters:   XMP_array - a XMP tree structure array as from read_XMP_array_from_text
+*
+* Returns:      output - the HTML string
+*
+******************************************************************************/
+
+function Interpret_XMP_to_HTML( $XMP_array )
+{
+        // Create a string to receive the output html
+        $output ="";
+
+        // Check if the XMP tree structure array is valid
+        if ( $XMP_array !== FALSE )
+        {
+                // Check if there is a rdf:RDF tag at either the first or second level
+                if ( ( $XMP_array[0]['tag'] ==  "x:xapmeta" ) && ( $XMP_array[0]['children'][0]['tag'] ==  "rdf:RDF" ) )
+                {
+                        // RDF found at second level - Save it's position
+                        $RDF_Contents = &$XMP_array[0]['children'][0]['children'];
+                }
+                else if ( ( $XMP_array[0]['tag'] ==  "x:xmpmeta" ) && ( $XMP_array[0]['children'][0]['tag'] ==  "rdf:RDF" ) )
+                {
+                        // RDF found at second level - Save it's position
+                        $RDF_Contents = &$XMP_array[0]['children'][0]['children'];
+                }
+                else if ( $XMP_array[0]['tag'] ==  "rdf:RDF" )
+                {
+                        // RDF found at first level - Save it's position
+                        $RDF_Contents = &$XMP_array[0]['children'];
+                }
+                else
+                {
+                        // RDF section not found - abort
+                        return "";
+                }
+
+                // Add heading to html output
+                $output .= "<h2 class=\"XMP_Main_Heading\">Contains Extensible Metadata Platform (XMP) / Resource Description Framework (RDF) Information</h2>\n";
+
+                // Cycle through each of the items in the RDF tree array, and process them
+                foreach ($RDF_Contents as $RDF_Item)
+                {
+                        // Check if the item is a rdf:Description tag - these are the only ones that can be processed
+
+                        if ( ( $RDF_Item['tag'] == "rdf:Description" ) && ( array_key_exists( 'children', $RDF_Item ) ) )
+                        {
+                                // Item is a rdf:Description tag.
+
+                                // Cycle through each of the attributes for this tag, looking
+                                // for a xmlns: attribute, which tells us what Namespace the
+                                // sub-items will be in.
+                                foreach( $RDF_Item['attributes'] as $key => $val )
+                                {
+                                        // Check for the xmlns: namespace attribute
+                                        if ( substr( $key,0,6) == "xmlns:" )
+                                        {
+                                                // Found a xmlns: attribute
+                                                // Extract the namespace string
+                                                // Add heading to the HTML according to which Namespace the RDF items have
+                                                switch ( substr( $key,6) )
+                                                {
+                                                        case "photoshop":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Photoshop RDF Segment</h3>\n";
+                                                                break;
+                                                        case "xapBJ":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Basic Job Ticket RDF Segment</h3>\n";
+                                                                break;
+                                                        case "xapMM":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Media Management RDF Segment</h3>\n";
+                                                                break;
+                                                        case "xapRights":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Rights Management RDF Segment</h3>\n";
+                                                                break;
+                                                        case "dc":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Dublin Core Metadata Initiative RDF Segment</h3>\n";
+                                                                break;
+                                                        case "xmp":
+                                                        case "xap":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP Basic Segment</h3>\n";
+                                                                break;
+                                                        case "xmpTPg":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP Paged-Text Segment</h3>\n";
+                                                                break;
+                                                        case "xmpTPg":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Adobe PDF Segment</h3>\n";
+                                                                break;
+                                                        case "tiff":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP - embedded TIFF Segment</h3>\n";
+                                                                break;
+                                                        case "exif":
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">XMP - embedded EXIF Segment</h3>\n";
+                                                                break;
+                                                        case "xapGImg":  // Sub Category - Do nothing
+                                                                break;
+                                                        case "stDim":  // Sub Category - Do nothing
+                                                                break;
+                                                        case "stEvt":  // Sub Category - Do nothing
+                                                                break;
+                                                        case "stRef":  // Sub Category - Do nothing
+                                                                break;
+                                                        case "stVer":  // Sub Category - Do nothing
+                                                                break;
+                                                        case "stJob":  // Sub Category - Do nothing
+                                                                break;
+
+                                                        default:
+                                                                $output .= "<h3 class=\"XMP_Secondary_Heading\">Unknown RDF Segment '" . substr( $key,6) . "'</h3>\n";
+                                                                break;
+                                                }
+
+
+                                        }
+
+                                }
+
+                                // Add the start of the table to the HTML output
+                                $output .= "\n<table  class=\"XMP_Table\" border=1>\n";
+
+
+                                // Check if this element has sub-items
+
+                                if ( array_key_exists( 'children', $RDF_Item ) )
+                                {
+
+                                        // Cycle through each of the sub-items
+                                        foreach( $RDF_Item['children'] as $child_item )
+                                        {
+                                                // Get an interpretation of the sub-item's caption and value
+                                                list($tag_caption, $value_str) = Interpret_RDF_Item( $child_item );
+
+                                                // Escape the text of the caption for html
+                                                $tag_caption = HTML_UTF8_Escape( $tag_caption );
+                                                // Escape the text of the value for html and turn newlines to <br>
+                                                $value_str = nl2br( HTML_UTF8_Escape( $value_str ) );
+
+                                                // Check if the value is empty - if it is, put a no-break-space in
+                                                // to ensure the table cell gets drawn
+                                                if ( $value_str == "" )
+                                                {
+                                                        $value_str = "&nbsp;";
+                                                }
+                                                // Add the table row to the output html
+                                                $output .= "<tr class=\"XMP_Table_Row\"><td  class=\"XMP_Caption_Cell\">" . $tag_caption . ":</td><td  class=\"XMP_Value_Cell\">" . $value_str . "</td></tr>\n";
+                                        }
+                                }
+
+                                // Add the end of the table to the html
+                                $output .= "\n</table>\n";
+
+
+                        }
+                        else
+                        {
+                                // Don't know how to process tags other than rdf:Description - do nothing
+                        }
+                }
+
+
+
+        }
+        // Return the resulting HTML
+        return $output;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_XMP_to_HTML
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+*         INTERNAL FUNCTIONS
+*
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     Interpret_RDF_Item
+*
+* Description:  Used by Interpret_XMP_to_HTML
+*               Used by get_RDF_field_html_value
+*               Used by interpret_RDF_collection
+*               Generates a caption and text representation of the value of a
+*               particular RDF item.
+*
+* Parameters:   Item - The RDF item to evaluate
+*
+* Returns:      tag_caption - the caption of the tag
+*               value_str - the text representation of the value
+*
+******************************************************************************/
+
+function Interpret_RDF_Item( $Item )
+{
+
+        // TODO: Many RDF items have not been tested - only photoshop 7.0 and CS items
+
+        // Create a string to receive the HTML output
+        $value_str = "";
+
+        // Check if the item has is in the lookup table of tag captions
+        if ( array_key_exists( $Item['tag'], $GLOBALS[ 'XMP_tag_captions' ] ) )
+        {
+                // Item is in list of captions, get the caption
+                $tag_caption = $GLOBALS[ 'XMP_tag_captions' ][ $Item['tag'] ];
+        }
+        else
+        {
+                // Item has no caption - make one
+                $tag_caption = "Unknown field " . $Item['tag'];
+        }
+
+
+        // Process specially the item according to it's tag
+        switch ( $Item['tag'] )
+        {
+
+                case "photoshop:DateCreated":            // This is in year month day order
+                        // Extract the year,month and day
+                        list( $year, $month, $day ) = sscanf( $Item['value'], "%d-%d-%d" );
+                        // Make a new date string with Day, Month, Year
+                        $value_str = "$day/$month/$year";
+                        break;
+
+                default :
+                        $value_str = get_RDF_field_html_value( $Item );
+                        break;
+        }
+
+
+
+
+        // Return the captiona and value
+        return array($tag_caption, $value_str);
+}
+
+
+/******************************************************************************
+* End of Function:     Interpret_RDF_Item
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     get_RDF_field_html_value
+*
+* Description:  Attempts to build a text representation of the value of an RDF
+*               item. This includes handling any collections or sub-resources.
+*
+* Parameters:   rdf_item - The RDF item to evaluate
+*
+* Returns:      output_str - the text representation of the field value
+*
+******************************************************************************/
+
+function get_RDF_field_html_value( $rdf_item )
+{
+        // Create a string to receive the output text
+        $output_str = "";
+
+        // Check if the item has a value
+        if ( array_key_exists( 'value', $rdf_item ) )
+        {
+                // The item does have a value - add it to the text
+                $output_str .= $rdf_item['value'];
+        }
+
+        // Check if the item has any attributes
+        if ( array_key_exists( 'attributes', $rdf_item ) )
+        {
+                // Cycle through each of the attributes
+                foreach( $rdf_item['attributes'] as $key => $val )
+                {
+                        // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator
+                        if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) )
+                        {
+                                // This item has a attribute indicating sub-resources
+                                // Check that the item has sub items
+                                if ( array_key_exists( 'children', $rdf_item ) )
+                                {
+                                        // The item does have sub-items,
+                                        // Cycle through each, Interpreting them and adding the result to the output text
+                                        foreach( $rdf_item['children'] as $child )
+                                        {
+                                                list($tag_caption, $value_str) = Interpret_RDF_Item( $child );
+                                                $output_str .= "$tag_caption  =  $value_str\n";
+                                        }
+                                        // The output text will have an extra \n on it - remove it
+                                        $output_str = rtrim( $output_str );
+                                }
+                        }
+                }
+        }
+                // If the item did not have sub-resources, it may still have sub-items - check for this
+        else if ( array_key_exists( 'children', $rdf_item ) )
+        {
+                // Non-resource Sub-items found, Cycle through each
+                foreach( $rdf_item['children'] as $child_item )
+                {
+                        // Check if this sub-item has a tag
+                        if ( array_key_exists( 'tag', $child_item ) )
+                        {
+                                // Sub item has a tag, Process it according to the tag
+                                switch ( $child_item[ 'tag' ] )
+                                {
+                                        // Collections
+                                        case "rdf:Alt":
+                                                $output_str .= "List of Alternates:\n";
+                                                $output_str .= interpret_RDF_collection( $child_item );
+                                                break;
+
+                                        case "rdf:Bag":
+                                                $output_str .= "Unordered List:\n";
+                                                $output_str .= interpret_RDF_collection( $child_item );
+                                                break;
+
+                                        case "rdf:Seq":
+                                                $output_str .= "Ordered List:\n";
+                                                $output_str .= interpret_RDF_collection( $child_item );
+                                                break;
+
+                                        // Sub-Resource
+                                        case "rdf:Description":
+                                                // Check that the item has sub items
+                                                if ( array_key_exists( 'children', $child_item ) )
+                                                {
+                                                        // The item does have sub-items,
+                                                        // Cycle through each, Interpreting them and adding the result to the output text
+                                                        foreach( $child_item['children'] as $child )
+                                                        {
+                                                                list($tag_caption, $value_str) = Interpret_RDF_Item( $child );
+                                                                $output_str .= "$tag_caption  =  $value_str\n";
+                                                        }
+                                                        // The output text will have an extra \n on it - remove it
+                                                        $output_str = rtrim( $output_str );
+                                                }
+                                                break;
+
+                                        // Other
+                                        default:
+                                                $output_str .= "Unknown Sub Item type:". $child_item[ 'tag' ]. "\n";
+                                                break;
+                                }
+                        } // sub-item Has no tags, look for a value
+                        else if ( array_key_exists( 'value', $child_item ) )
+                        {
+                                $output_str .= $rdf_item['value'] . "\n";
+                        }
+                        else
+                        {
+                                // no info - do nothing
+                        }
+
+                }
+        }
+
+        // return the resulting value string
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     get_RDF_field_html_value
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     interpret_RDF_collection
+*
+* Description:  Attempts to build a text representation of the value of an RDF
+*               collection item. This includes handling any sub-collections or
+*               sub-resources.
+*
+* Parameters:   rdf_item - The RDF collection item to evaluate
+*
+* Returns:      output_str - the text representation of the collection value
+*
+******************************************************************************/
+
+function interpret_RDF_collection( $item )
+{
+        // Create a string to receive the output
+        $output_str = "";
+
+        // Check if the collection item has sub-items
+        if ( array_key_exists( 'children', $item ) )
+        {
+
+                // Cycle through each of the sub-items
+                foreach( $item['children'] as $list_item )
+                {
+                        // Check that the sub item has a tag, and don't process it if it doesn't
+                        if ( ! array_key_exists( 'tag', $list_item ) )
+                        {
+                                continue 1;
+                        }
+
+                        // Check that the sub-item tag is either rdf:li or rdf:_1 ....
+                        // This signifies it is a list item of the collection
+                        if ( ( $list_item['tag'] == "rdf:li" ) ||
+                             ( preg_match ( "rdf:_\d+", $list_item['tag'] ) == 1 ) )
+                        {
+                                // A List item has been found
+                                // Check if there are sub-resources,
+                                // starting by checking if there are attributes
+                                if ( array_key_exists( 'attributes', $list_item ) )
+                                {
+                                        // Cycle through each of the attributes
+                                        foreach( $list_item['attributes'] as $key => $val )
+                                        {
+                                                // Check if this attribute is rdf:parseType = 'Resource' i.e. a sub-resource indicator
+                                                if ( ( $key == "rdf:parseType" ) && ( $val == "Resource" ) )
+                                                {
+                                                        // This item has a attribute indicating sub-resources
+                                                        // Check that the item has sub items
+                                                        if ( array_key_exists( 'children', $list_item ) )
+                                                        {
+                                                                // The item does have sub-items,
+                                                                // Cycle through each, Interpreting them and adding the result to the output text
+                                                                foreach( $list_item['children'] as $child )
+                                                                {
+                                                                        list($tag_caption, $value_str) = Interpret_RDF_Item( $child );
+                                                                        $output_str .= "$tag_caption  =  $value_str\n";
+                                                                }
+                                                                // The output text will have an extra \n on it - remove it
+                                                                $output_str = rtrim( $output_str );
+                                                        }
+                                                }
+                                        }
+                                }
+
+                                // Check if the list item has a value
+                                if ( array_key_exists( 'value', $list_item ) )
+                                {
+                                        // Value found, add it to the output
+                                        $output_str .= get_RDF_field_html_value( $list_item ) . "\n";
+                                }
+
+                        }
+                }
+                // The list of sub-items formed will have a trailing \n, remove it.
+                $output_str = rtrim( $output_str );
+
+        }
+        else
+        {
+                // No sub-items in collection - can't do anything
+        }
+
+        // Return the output value
+        return $output_str;
+
+}
+
+/******************************************************************************
+* End of Function:     interpret_RDF_collection
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      XMP_tag_captions
+*
+* Contents:     The Captions of the known XMP fields, indexed by their field name
+*
+******************************************************************************/
+
+$GLOBALS[ 'XMP_tag_captions' ] = array (
+
+"dc:contributor" => "Other Contributor(s)",
+"dc:coverage" => "Coverage (scope)",
+"dc:creator" => "Creator(s) (Authors)",
+"dc:date" => "Date",
+"dc:description" => "Description (Caption)",
+"dc:format" => "MIME Data Format",
+"dc:identifier" => "Unique Resource Identifer",
+"dc:language" => "Language(s)",
+"dc:publisher" => "Publisher(s)",
+"dc:relation" => "Relations to other documents",
+"dc:rights" => "Rights Statement",
+"dc:source" => "Source (from which this Resource is derived)",
+"dc:subject" => "Subject and Keywords",
+"dc:title" => "Title",
+"dc:type" => "Resource Type",
+
+"xmp:Advisory" => "Externally Editied Properties",
+"xmp:BaseURL" => "Base URL for relative URL's",
+"xmp:CreateDate" => "Original Creation Date",
+"xmp:CreatorTool" => "Creator Tool",
+"xmp:Identifier" => "Identifier(s)",
+"xmp:MetadataDate" => "Metadata Last Modify Date",
+"xmp:ModifyDate" => "Resource Last Modify Date",
+"xmp:Nickname" => "Nickname",
+"xmp:Thumbnails" => "Thumbnails",
+
+"xmpidq:Scheme" => "Identification Scheme",
+
+// These are not in spec but Photoshop CS seems to use them
+"xap:Advisory" => "Externally Editied Properties",
+"xap:BaseURL" => "Base URL for relative URL's",
+"xap:CreateDate" => "Original Creation Date",
+"xap:CreatorTool" => "Creator Tool",
+"xap:Identifier" => "Identifier(s)",
+"xap:MetadataDate" => "Metadata Last Modify Date",
+"xap:ModifyDate" => "Resource Last Modify Date",
+"xap:Nickname" => "Nickname",
+"xap:Thumbnails" => "Thumbnails",
+"xapidq:Scheme" => "Identification Scheme",
+
+
+"xapRights:Certificate" => "Certificate",
+"xapRights:Copyright" => "Copyright",
+"xapRights:Marked" => "Marked",
+"xapRights:Owner" => "Owner",
+"xapRights:UsageTerms" => "Legal Terms of Usage",
+"xapRights:WebStatement" => "Web Page describing rights statement (Owner URL)",
+
+"xapMM:ContainedResources" => "Contained Resources",
+"xapMM:ContributorResources" => "Contributor Resources",
+"xapMM:DerivedFrom" => "Derived From",
+"xapMM:DocumentID" => "Document ID",
+"xapMM:History" => "History",
+"xapMM:LastURL" => "Last Written URL",
+"xapMM:ManagedFrom" => "Managed From",
+"xapMM:Manager" => "Asset Management System",
+"xapMM:ManageTo" => "Manage To",
+"xapMM:xmpMM:ManageUI" => "Managed Resource URI",
+"xapMM:ManagerVariant" => "Particular Variant of Asset Management System",
+"xapMM:RenditionClass" => "Rendition Class",
+"xapMM:RenditionParams" => "Rendition Parameters",
+"xapMM:RenditionOf" => "Rendition Of",
+"xapMM:SaveID" => "Save ID",
+"xapMM:VersionID" => "Version ID",
+"xapMM:Versions" => "Versions",
+
+"xapBJ:JobRef" => "Job Reference",
+
+"xmpTPg:MaxPageSize" => "Largest Page Size",
+"xmpTPg:NPages" => "Number of pages",
+
+"pdf:Keywords" => "Keywords",
+"pdf:PDFVersion" => "PDF file version",
+"pdf:Producer" => "PDF Creation Tool",
+
+"photoshop:AuthorsPosition" => "Authors Position",
+"photoshop:CaptionWriter" => "Caption Writer",
+"photoshop:Category" => "Category",
+"photoshop:City" => "City",
+"photoshop:Country" => "Country",
+"photoshop:Credit" => "Credit",
+"photoshop:DateCreated" => "Creation Date",
+"photoshop:Headline" => "Headline",
+"photoshop:History" => "History",                       // Not in XMP spec
+"photoshop:Instructions" => "Instructions",
+"photoshop:Source" => "Source",
+"photoshop:State" => "State",
+"photoshop:SupplementalCategories" => "Supplemental Categories",
+"photoshop:TransmissionReference" => "Technical (Transmission) Reference",
+"photoshop:Urgency" => "Urgency",
+
+
+"tiff:ImageWidth" => "Image Width",
+"tiff:ImageLength" => "Image Length",
+"tiff:BitsPerSample" => "Bits Per Sample",
+"tiff:Compression" => "Compression",
+"tiff:PhotometricInterpretation" => "Photometric Interpretation",
+"tiff:Orientation" => "Orientation",
+"tiff:SamplesPerPixel" => "Samples Per Pixel",
+"tiff:PlanarConfiguration" => "Planar Configuration",
+"tiff:YCbCrSubSampling" => "YCbCr Sub-Sampling",
+"tiff:YCbCrPositioning" => "YCbCr Positioning",
+"tiff:XResolution" => "X Resolution",
+"tiff:YResolution" => "Y Resolution",
+"tiff:ResolutionUnit" => "Resolution Unit",
+"tiff:TransferFunction" => "Transfer Function",
+"tiff:WhitePoint" => "White Point",
+"tiff:PrimaryChromaticities" => "Primary Chromaticities",
+"tiff:YCbCrCoefficients" => "YCbCr Coefficients",
+"tiff:ReferenceBlackWhite" => "Black & White Reference",
+"tiff:DateTime" => "Date & Time",
+"tiff:ImageDescription" => "Image Description",
+"tiff:Make" => "Make",
+"tiff:Model" => "Model",
+"tiff:Software" => "Software",
+"tiff:Artist" => "Artist",
+"tiff:Copyright" => "Copyright",
+
+
+"exif:ExifVersion" => "Exif Version",
+"exif:FlashpixVersion" => "Flash pix Version",
+"exif:ColorSpace" => "Color Space",
+"exif:ComponentsConfiguration" => "Components Configuration",
+"exif:CompressedBitsPerPixel" => "Compressed Bits Per Pixel",
+"exif:PixelXDimension" => "Pixel X Dimension",
+"exif:PixelYDimension" => "Pixel Y Dimension",
+"exif:MakerNote" => "Maker Note",
+"exif:UserComment" => "User Comment",
+"exif:RelatedSoundFile" => "Related Sound File",
+"exif:DateTimeOriginal" => "Date & Time of Original",
+"exif:DateTimeDigitized" => "Date & Time Digitized",
+"exif:ExposureTime" => "Exposure Time",
+"exif:FNumber" => "F Number",
+"exif:ExposureProgram" => "Exposure Program",
+"exif:SpectralSensitivity" => "Spectral Sensitivity",
+"exif:ISOSpeedRatings" => "ISO Speed Ratings",
+"exif:OECF" => "Opto-Electronic Conversion Function",
+"exif:ShutterSpeedValue" => "Shutter Speed Value",
+"exif:ApertureValue" => "Aperture Value",
+"exif:BrightnessValue" => "Brightness Value",
+"exif:ExposureBiasValue" => "Exposure Bias Value",
+"exif:MaxApertureValue" => "Max Aperture Value",
+"exif:SubjectDistance" => "Subject Distance",
+"exif:MeteringMode" => "Metering Mode",
+"exif:LightSource" => "Light Source",
+"exif:Flash" => "Flash",
+"exif:FocalLength" => "Focal Length",
+"exif:SubjectArea" => "Subject Area",
+"exif:FlashEnergy" => "Flash Energy",
+"exif:SpatialFrequencyResponse" => "Spatial Frequency Response",
+"exif:FocalPlaneXResolution" => "Focal Plane X Resolution",
+"exif:FocalPlaneYResolution" => "Focal Plane Y Resolution",
+"exif:FocalPlaneResolutionUnit" => "Focal Plane Resolution Unit",
+"exif:SubjectLocation" => "Subject Location",
+"exif:SensingMethod" => "Sensing Method",
+"exif:FileSource" => "File Source",
+"exif:SceneType" => "Scene Type",
+"exif:CFAPattern" => "Colour Filter Array Pattern",
+"exif:CustomRendered" => "Custom Rendered",
+"exif:ExposureMode" => "Exposure Mode",
+"exif:WhiteBalance" => "White Balance",
+"exif:DigitalZoomRatio" => "Digital Zoom Ratio",
+"exif:FocalLengthIn35mmFilm" => "Focal Length In 35mm Film",
+"exif:SceneCaptureType" => "Scene Capture Type",
+"exif:GainControl" => "Gain Control",
+"exif:Contrast" => "Contrast",
+"exif:Saturation" => "Saturation",
+"exif:Sharpness" => "Sharpness",
+"exif:DeviceSettingDescription" => "Device Setting Description",
+"exif:SubjectDistanceRange" => "Subject Distance Range",
+"exif:ImageUniqueID" => "Image Unique ID",
+"exif:GPSVersionID" => "GPS Version ID",
+"exif:GPSLatitude" => "GPS Latitude",
+"exif:GPSLongitude" => "GPS Longitude",
+"exif:GPSAltitudeRef" => "GPS Altitude Reference",
+"exif:GPSAltitude" => "GPS Altitude",
+"exif:GPSTimeStamp" => "GPS Time Stamp",
+"exif:GPSSatellites" => "GPS Satellites",
+"exif:GPSStatus" => "GPS Status",
+"exif:GPSMeasureMode" => "GPS Measure Mode",
+"exif:GPSDOP" => "GPS Degree Of Precision",
+"exif:GPSSpeedRef" => "GPS Speed Reference",
+"exif:GPSSpeed" => "GPS Speed",
+"exif:GPSTrackRef" => "GPS Track Reference",
+"exif:GPSTrack" => "GPS Track",
+"exif:GPSImgDirectionRef" => "GPS Image Direction Reference",
+"exif:GPSImgDirection" => "GPS Image Direction",
+"exif:GPSMapDatum" => "GPS Map Datum",
+"exif:GPSDestLatitude" => "GPS Destination Latitude",
+"exif:GPSDestLongitude" => "GPS Destnation Longitude",
+"exif:GPSDestBearingRef" => "GPS Destination Bearing Reference",
+"exif:GPSDestBearing" => "GPS Destination Bearing",
+"exif:GPSDestDistanceRef" => "GPS Destination Distance Reference",
+"exif:GPSDestDistance" => "GPS Destination Distance",
+"exif:GPSProcessingMethod" => "GPS Processing Method",
+"exif:GPSAreaInformation" => "GPS Area Information",
+"exif:GPSDifferential" => "GPS Differential",
+
+"stDim:w" => "Width",
+"stDim:h" => "Height",
+"stDim:unit" => "Units",
+
+"xapGImg:height" => "Height",
+"xapGImg:width" => "Width",
+"xapGImg:format" => "Format",
+"xapGImg:image" => "Image",
+
+"stEvt:action" => "Action",
+"stEvt:instanceID" => "Instance ID",
+"stEvt:parameters" => "Parameters",
+"stEvt:softwareAgent" => "Software Agent",
+"stEvt:when" => "When",
+
+"stRef:instanceID" => "Instance ID",
+"stRef:documentID" => "Document ID",
+"stRef:versionID" => "Version ID",
+"stRef:renditionClass" => "Rendition Class",
+"stRef:renditionParams" => "Rendition Parameters",
+"stRef:manager" => "Asset Management System",
+"stRef:managerVariant" => "Particular Variant of Asset Management System",
+"stRef:manageTo" => "Manage To",
+"stRef:manageUI" => "Managed Resource URI",
+
+"stVer:comments" => "",
+"stVer:event" => "",
+"stVer:modifyDate" => "",
+"stVer:modifier" => "",
+"stVer:version" => "",
+
+
+
+"stJob:name" => "Job Name",
+"stJob:id" => "Unique Job ID",
+"stJob:url" => "URL for External Job Management File",
+
+// Exif Flash
+"exif:Fired" => "Fired",
+"exif:Return" => "Return",
+"exif:Mode" => "Mode",
+"exif:Function" => "Function",
+"exif:RedEyeMode" => "Red Eye Mode",
+
+// Exif OECF/SFR
+"exif:Columns" => "Columns",
+"exif:Rows" => "Rows",
+"exif:Names" => "Names",
+"exif:Values" => "Values",
+
+// Exif CFAPattern
+"exif:Columns" => "Columns",
+"exif:Rows" => "Rows",
+"exif:Values" => "Values",
+
+
+// Exif DeviceSettings
+"exif:Columns" => "Columns",
+"exif:Rows" => "Rows",
+"exif:Settings" => "Settings",
+
+
+
+);
+
+/******************************************************************************
+* End of Global Variable:     XMP_tag_captions
+******************************************************************************/
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/get_casio_thumb.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/get_casio_thumb.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/get_casio_thumb.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/get_casio_thumb.php	2004/08/05 16:08:34
@@ -0,0 +1,126 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     get_ps_thumb.php
+*
+* Description:  This script extracts a Casio Type 2 EXIF Makernote Thumbnail
+*               from within a JPEG file and allows it to be displayed
+*
+* Usage:        get_casio_thumb?filename=<filename>
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+        // Ensure that nothing can write to the standard io, before we get the header out
+        ob_start( );
+
+
+        include 'EXIF.php';
+
+
+        // retrieve the filename from the URL parameters
+
+        $filename = $GLOBALS['HTTP_GET_VARS']['filename'];
+
+
+        // Retrieve any EXIF data in the file
+
+        $Exif_array = get_EXIF_JPEG( $filename );
+
+        // Check if any EXIF data was retrieved
+        if ( $Exif_array === FALSE )
+        {
+                // No EXIF data was found - abort
+                ob_end_clean ( );
+                echo "<p>Error getting EXIF Information</p>\n";
+                return;
+        }
+
+
+        // Check that there is at least the Zeroth IFD in the array
+        if ( count( $Exif_array ) < 1  )
+        {
+                // Couldn't find the zeroth IFD
+                return;
+        }
+        
+        
+        // Check that the EXIF IFD exists
+        if ( array_key_exists( 34665, $Exif_array[0] ) )
+        {
+                // Found the EXIF IFD,
+                
+                // Check that the makernote tag exists
+                if ( array_key_exists( 37500, $Exif_array[0][34665]['Data'][0] ) )
+                {
+                        // Makernote Exists
+                
+                        // Check that the Makernote is Casio Type 2
+                        if  ( $Exif_array[0][34665]['Data'][0][37500]['Makernote Tags'] == "Casio Type 2" )
+                        {
+                                // Makernote is Casio Type 2
+                                // Check if an IFD exists for the makernote
+                                if ( array_key_exists( 0, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'] ) )
+                                {
+                                        // Check if the Thumbnail offset tag 8192 exists
+                                        if ( array_key_exists( 8192, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) )
+                                        {
+                                                // Found Thumbnail - Display it
+                                                ob_end_clean ( );
+                                                header("Content-type: image/jpeg");
+                                                print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][8192]['Data'];
+                                        }
+                                                // Check if the Thumbnail offset tag 4 exists
+                                        else if ( array_key_exists( 4, $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0] ) )
+                                        {
+                                                // Found Thumbnail - Display it
+                                                ob_end_clean ( );
+                                                header("Content-type: image/jpeg");
+                                                print $Exif_array[0][34665]['Data'][0][37500]['Decoded Data'][0][4]['Data'];
+                                        }
+                                }
+
+                        }
+                }
+        }
+        else
+        {
+        }
+
+        return;
+        
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Toolkit_Version.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Toolkit_Version.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Toolkit_Version.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Toolkit_Version.php	2004/11/25 13:19:40
@@ -0,0 +1,50 @@
+<?php
+/******************************************************************************
+*
+* Filename:     XMP.php
+*
+* Description:  Provides a single place where the current version of the toolkit
+*               is stored.
+*
+* Author:       Evan Hunter
+*
+* Date:         27/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.11
+*
+* NOTE:         This file will change with every revision update, hence will not
+*               be shown in the changes list
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+$GLOBALS['Toolkit_Version'] = "1.11";
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/PIM.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/PIM.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/PIM.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/PIM.php	2004/08/09 17:04:56
@@ -0,0 +1,279 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     PIM.php
+*
+* Description:  Provides functions for reading, writing and interpreting a
+*               Print Image Matching information data block.
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+include_once "EXIF.php";
+
+// TODO Find out definitions of Print Image Matching Info tags
+
+
+/******************************************************************************
+*
+* Function:     Decode_PIM
+*
+* Description:  Decodes the contents of a EXIF tag containing Print Image
+*               Matching information, and returns the contents as an array
+*
+* Parameters:   tag - An EXIF tag containing Print Image Matching information
+*                     as from get_EXIF_JPEG
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+* Returns:      newtag - The EXIF tag, modified with the data field containing
+*                        an array of the PIM contents
+*
+******************************************************************************/
+
+function Decode_PIM( $tag, $Tag_Definitions_Name )
+{
+
+        // Create a new EXIF tag for the output
+        $newtag = $tag;
+
+        // Check that this tag is for Print Image Matching Info
+        if ( $tag['Type'] == "PIM" )
+        {
+
+                // Check that the data starts with PrintIM
+                if ( substr( $tag['Data'], 0, 8 ) == "PrintIM\x00" )
+                {
+
+                        // Find the end of the version string
+                        if ( ( $ver_pos = strpos ( $tag['Data'], "\0", 8 ) ) == -1 )
+                        {
+                                // couldn't find the start of the version string
+                                return $newtag;
+                        }
+                        
+                        // Create an array to receive the Data
+                        $newtag['Data'] = array( );
+
+                        // Extract the PrintIM version
+                        $newtag['Data']['Version'] = substr( $tag['Data'], 8, $ver_pos - 8 );
+                        // Skip the position over the version
+                        $count_pos =  $ver_pos+2;
+                        
+                        // Extract the count of tags - 2 bytes
+                        $PI_tag_count = get_IFD_Data_Type( substr($tag['Data'], $count_pos, 2) , 3, $tag['Byte Align'] );
+
+                        // Panasonic have put an extra Null after the Version, which
+                        // causes the tag count to be wrong -
+                        // check if it is zero - i.e. possibly wrong
+                        if ( ( $PI_tag_count == 0 ) )
+                        {
+                                // Tag count is zero - try moving the position by one,
+                                // then re-extracting the count
+                                $count_pos++;
+                                $PI_tag_count = get_IFD_Data_Type( substr($tag['Data'], $count_pos, 2) , 3, $tag['Byte Align'] );
+                        }
+
+                        // Extract the data part of the PrintIM block
+                        $data_part = substr($tag['Data'], $count_pos+2);
+
+                        // Cycle through each tag
+                        for ( $a = 0; $a < $PI_tag_count; $a++ )
+                        {
+                                // Read the tag number - 2 bytes
+                                $PI_tag = get_IFD_Data_Type( substr($data_part, $a*6, 2) , 3, $tag['Byte Align'] );
+                                
+                                // Read the tag data - 4 bytes
+                                $newtag['Data'][ ] = array( 'Tag Number' => $PI_tag, 'Data' => substr($data_part, $a*6+2, 4) , 'Decoded' => False );
+                        }
+                }
+                
+        }
+
+        // Return the updated tag
+        return $newtag;
+        
+}
+
+/******************************************************************************
+* End of Function:     Decode_PIM
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     Encode_PIM
+*
+* Description:  Encodes the contents of a EXIF tag containing Print Image
+*               Matching information, and returns the contents as a packed binary string
+*
+* Parameters:   tag - An EXIF tag containing Print Image Matching information
+*                     as from get_EXIF_JPEG
+*               Byte_Align - the Byte alignment to use - "MM" or "II"
+*
+* Returns:      packed_data - The packed binary string representing the PIM data
+*
+******************************************************************************/
+
+function Encode_PIM( $tag, $Byte_Align)
+{
+
+        // Create a string to receive the packed data
+        $packed_data = "";
+
+        // Check that this tag is for Print Image Matching Info
+        if ( $tag['Type'] == "PIM" )
+        {
+                // Check that the tag has been decoded - otherwise we don't need to do anything
+                if ( ( is_array( $tag['Data'] ) ) &&
+                     ( count ( $tag['Data'] ) > 0 ) )
+                {
+                        // Add the header to the packed data
+                        $packed_data .= "PrintIM\x00";
+                        
+                        // Add the version to the packed data
+                        $packed_data .= $tag['Data']['Version'] . "\x00";
+
+                        // Create a string to receive the tag data
+                        $tag_data_str = "";
+                        
+                        // Cycle through each tag
+                        $tag_count = 0;
+                        foreach( $tag['Data'] as $key => $curr_tag )
+                        {
+                                // Make sure this is a tag and not supplementary info
+                                if ( is_numeric( $key ) )
+                                {
+                                        // Count how many tags are created
+                                        $tag_count++;
+
+                                        // Add the tag number to the packed tag data
+                                        $tag_data_str .= put_IFD_Data_Type( $curr_tag['Tag Number'], 3, $Byte_Align );
+
+                                        // Add the tag data to the packed tag data
+                                        $tag_data_str .= $curr_tag['Data'];
+                                }
+                        }
+                        
+                        // Add the tag count to the packed data
+                        $packed_data .= put_IFD_Data_Type( $tag_count, 3, $Byte_Align );
+                        
+                        // Add the packed tag data to the packed data
+                        $packed_data .= $tag_data_str;
+                }
+        }
+                        
+        // Return the resulting packed data
+        return $packed_data;
+
+}
+
+/******************************************************************************
+* End of Function:     Encode_PIM
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_PIM_Text_Value
+*
+* Description:  Interprets the contents of a EXIF tag containing Print Image
+*               Matching information, and returns content as as a text string
+*
+* Parameters:   tag - An EXIF tag containing Print Image Matching information
+*                     as from get_EXIF_JPEG
+*               Tag_Definitions_Name - The name of the Tag Definitions group
+*                                      within the global array IFD_Tag_Definitions
+*
+* Returns:      output_str - The text string representing the PIM info
+*
+******************************************************************************/
+
+function get_PIM_Text_Value( $Tag, $Tag_Definitions_Name )
+{
+
+        // Create a string to receive the output
+        $output_str = "";
+        
+        // Check if the PIM tag has been decoded
+        if ( ( is_array( $Tag['Data'] ) ) &&
+             ( count ( $Tag['Data'] ) > 0 ) )
+        {
+                // The tag has been decoded
+
+                // Add the Version to the output
+                $output_str = "Version: " . $Tag['Data']['Version'] . "\n";
+                
+                // Check if the user wants to hide unknown tags
+                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == FALSE )
+                {
+                        // The user wants to see unknown tags
+                        // Cycle through each tag
+                        foreach ( $Tag['Data'] as $PIM_tag_Key => $PIM_tag )
+                        {
+                                // Check that the tag is not the version array element
+                                if ( $PIM_tag_Key !== 'Version' )
+                                {
+                                        // Add the tag to the output
+                                        $output_str .= "Unknown Tag " . $PIM_tag['Tag Number'] . ": (" . strlen( $PIM_tag['Data'] ) . " bytes of data)\n";
+                                }
+                        }
+                }
+        }
+        
+        // Return the output text
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     get_PIM_Text_Value
+******************************************************************************/
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/IPTC.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/IPTC.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/IPTC.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/IPTC.php	2004/11/22 21:26:56
@@ -0,0 +1,691 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     IPTC.php
+*
+* Description:  Provides functions for reading and writing IPTC-NAA Information
+*               Interchange Model metadata
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.10
+*
+* Changes:      1.00 -> 1.01 : changed get_IPTC to return partial data when error occurs
+*               1.01 -> 1.10 : changed put_IPTC to check if the incoming IPTC block is valid
+*                              changed Interpret_IPTC_to_HTML, adding nl2br functions for each text field,
+*                              so that multiline text displays properly
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+// TODO: Not all of the IPTC fields have been tested properly
+
+/******************************************************************************
+*
+* Function:     get_IPTC
+*
+* Description:  Extracts IPTC-NAA IIM data from the string provided, and returns
+*               the information as an array
+*
+* Parameters:   Data_Str - the string containing the IPTC-NAA IIM records. Must
+*                          be exact length of the IPTC-NAA IIM data.
+*
+* Returns:      OutputArray - Array of IPTC-NAA IIM records
+*               FALSE - If an error occured in decoding
+*
+******************************************************************************/
+
+function get_IPTC( $Data_Str )
+{
+
+        // Initialise the start position
+        $pos = 0;
+        // Create the array to receive the data
+        $OutputArray = array( );
+
+        // Cycle through the IPTC records, decoding and storing them
+        while( $pos < strlen($Data_Str) )
+        {
+                // TODO - Extended Dataset record not supported
+
+                // Check if there is sufficient data for reading the record
+                if ( strlen( substr($Data_Str,$pos) ) < 5 )
+                {
+                        // Not enough data left for a record - Probably corrupt data - ERROR
+                        // Change: changed to return partial data as of revision 1.01
+                        return $OutputArray;
+                }
+
+                // Unpack data from IPTC record:
+                // First byte - IPTC Tag Marker - always 28
+                // Second byte - IPTC Record Number
+                // Third byte - IPTC Dataset Number
+                // Fourth and fifth bytes - two byte size value
+                $iptc_raw = unpack( "CIPTC_Tag_Marker/CIPTC_Record_No/CIPTC_Dataset_No/nIPTC_Size", substr($Data_Str,$pos) );
+
+                // Skip position over the unpacked data
+                $pos += 5;
+
+                // Construct the IPTC type string eg 2:105
+                $iptctype = sprintf( "%01d:%02d", $iptc_raw['IPTC_Record_No'], $iptc_raw['IPTC_Dataset_No']);
+
+                // Check if there is sufficient data for reading the record contents
+                if ( strlen( substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) ) !== $iptc_raw['IPTC_Size'] )
+                {
+                        // Not enough data left for the record content - Probably corrupt data - ERROR
+                        // Change: changed to return partial data as of revision 1.01
+                        return $OutputArray;
+                }
+
+                // Add the IPTC record to the output array
+                $OutputArray[] = array( "IPTC_Type" => $iptctype ,
+                                        "RecName" => $GLOBALS[ "IPTC_Entry_Names" ][ $iptctype ],
+                                        "RecDesc" => $GLOBALS[ "IPTC_Entry_Descriptions" ][ $iptctype ],
+                                        "RecData" => substr( $Data_Str, $pos, $iptc_raw['IPTC_Size'] ) );
+
+                // Skip over the IPTC record data
+                $pos += $iptc_raw['IPTC_Size'];
+        }
+        return $OutputArray;
+
+}
+
+
+/******************************************************************************
+* End of Function:     get_IPTC
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_IPTC
+*
+* Description:  Encodes an array of IPTC-NAA records into a string encoded
+*               as IPTC-NAA IIM. (The reverse of get_IPTC)
+*
+* Parameters:   new_IPTC_block - the IPTC-NAA array to be encoded. Should be
+*                                the same format as that received from get_IPTC
+*
+* Returns:      iptc_packed_data - IPTC-NAA IIM encoded string
+*
+******************************************************************************/
+
+
+function put_IPTC( $new_IPTC_block )
+{
+        // Check if the incoming IPTC block is valid
+        if ( $new_IPTC_block == FALSE )
+        {
+                // Invalid IPTC block - abort
+                return FALSE;
+        }
+        // Initialise the packed output data string
+        $iptc_packed_data = "";
+
+        // Cycle through each record in the new IPTC block
+        foreach ($new_IPTC_block as $record)
+        {
+                // Extract the Record Number and Dataset Number from the IPTC_Type field
+                list($IPTC_Record, $IPTC_Dataset) = sscanf( $record['IPTC_Type'], "%d:%d");
+
+                // Write the IPTC-NAA IIM Tag Marker, Record Number, Dataset Number and Data Size to the packed output data string
+                $iptc_packed_data .= pack( "CCCn", 28, $IPTC_Record, $IPTC_Dataset, strlen($record['RecData']) );
+
+                // Write the IPTC-NAA IIM Data to the packed output data string
+                $iptc_packed_data .= $record['RecData'];
+        }
+
+        // Return the IPTC-NAA IIM data
+        return $iptc_packed_data;
+}
+
+/******************************************************************************
+* End of Function:     put_IPTC
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_IPTC_to_HTML
+*
+* Description:  Generates html detailing the contents a IPTC-NAA IIM array
+*               which was retrieved with the get_IPTC function
+*
+* Parameters:   IPTC_info - the IPTC-NAA IIM array,as read from get_IPTC
+*
+* Returns:      OutputStr - A string containing the HTML
+*
+******************************************************************************/
+
+function Interpret_IPTC_to_HTML( $IPTC_info )
+{
+        // Create a string to receive the HTML
+        $output_str ="";
+
+        // Check if the IPTC
+        if ( $IPTC_info !== FALSE )
+        {
+
+
+                // Add Heading to HTML
+                $output_str .= "<h3 class=\"IPTC_Main_Heading\">IPTC-NAA Record</h3>\n";
+
+                // Add Table to HTML
+                $output_str .= "\n<table class=\"IPTC_Table\" border=1>\n";
+
+                // Cycle through each of the IPTC-NAA IIM records
+                foreach( $IPTC_info as $IPTC_Record )
+                {
+                        // Check if the record is a known IPTC field
+                        $Record_Name = $IPTC_Record['RecName'];
+                        if ( $Record_Name == "" )
+                        {
+                                // Record is an unknown field - add message to HTML
+                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">Unknown IPTC field '". htmlentities( $IPTC_Record['IPTC_Type'] ). "' :</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                        }
+                        else
+                        {
+                                // Record is a recognised IPTC field - Process it accordingly
+
+                                switch ( $IPTC_Record['IPTC_Type'] )
+                                {
+                                        case "1:00":    // Envelope Record:Model Version
+                                        case "1:22":    // Envelope Record:File Format Version
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                break;
+
+                                        case "1:90":    // Envelope Record:Coded Character Set
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Decoding not yet implemented<br>\n (Hex Data: " . bin2hex( $IPTC_Record['RecData'] )  .")</td></tr>\n";
+                                                break;
+                                                // TODO: Implement decoding of IPTC record 1:90
+
+                                        case "1:20":    // Envelope Record:File Format
+
+                                                $formatno = hexdec( bin2hex( $IPTC_Record['RecData'] ) );
+
+                                                // Lookup file format from lookup-table
+                                                if ( array_key_exists( $formatno, $GLOBALS[ "IPTC_File Formats" ] ) )
+                                                {
+                                                        // Entry was found in lookup table - add it to HTML
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">File Format</td><td class=\"IPTC_Value_Cell\">". $GLOBALS[ "IPTC_File Formats" ][$formatno] . "</td></tr>\n";
+                                                }
+                                                else
+                                                {
+                                                        // No matching entry was found in lookup table - add message to html
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">File Format</td><td class=\"IPTC_Value_Cell\">Unknown File Format ($formatno)</td></tr>\n";
+                                                }
+                                                break;
+
+
+                                        case "2:00":    // Application Record:Record Version
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">IPTC Version</td><td class=\"IPTC_Value_Cell\">" . hexdec( bin2hex( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                break;
+
+                                        case "2:42":    // Application Record: Action Advised
+
+                                                // Looup Action
+                                                if ( $IPTC_Record['RecData'] == "01" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Kill</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "02" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Replace</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "03" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Append</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "04" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Reference</td></tr>\n";
+                                                }
+                                                else
+                                                {
+                                                        // Unknown Action
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                }
+                                                break;
+
+                                        case "2:08":    // Application Record:Editorial Update
+                                                if ( $IPTC_Record['RecData'] == "01" )
+                                                {
+                                                        // Additional Language
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Additional language</td></tr>\n";
+                                                }
+                                                else
+                                                {
+                                                        // Unknown Value
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                }
+                                                break;
+
+                                        case "2:30":    // Application Record:Release Date
+                                        case "2:37":    // Application Record:Expiration Date
+                                        case "2:47":    // Application Record:Reference Date
+                                        case "2:55":    // Application Record:Date Created
+                                        case "2:62":    // Application Record:Digital Creation Date
+                                        case "1:70":    // Envelope Record:Date Sent
+                                                $date_array = unpack( "a4Year/a2Month/A2Day", $IPTC_Record['RecData'] );
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $date_array['Day'] . "/" . $date_array['Month'] . "/" . $date_array['Year'] ) ) ."</td></tr>\n";
+                                                break;
+
+                                        case "2:35":    // Application Record:Release Time
+                                        case "2:38":    // Application Record:Expiration Time
+                                        case "2:60":    // Application Record:Time Created
+                                        case "2:63":    // Application Record:Digital Creation Time
+                                        case "1:80":    // Envelope Record:Time Sent
+                                                $time_array = unpack( "a2Hour/a2Minute/A2Second/APlusMinus/A4Timezone", $IPTC_Record['RecData'] );
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" . nl2br( HTML_UTF8_Escape( $time_array['Hour'] . ":" . $time_array['Minute'] . ":" . $time_array['Second'] . " ". $time_array['PlusMinus'] . $time_array['Timezone'] ) ) ."</td></tr>\n";
+                                                break;
+
+                                        case "2:75":    // Application Record:Object Cycle
+                                                // Lookup Value
+                                                if ( $IPTC_Record['RecData'] == "a" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Morning</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "p" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Evening</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "b" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Both Morning and Evening</td></tr>\n";
+                                                }
+                                                else
+                                                {
+                                                        // Unknown Value
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                }
+                                                break;
+
+                                        case "2:125":   // Application Record:Rasterised Caption
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">460x128 pixel black and white caption image</td></tr>\n";
+                                                break;
+                                                // TODO: Display Rasterised Caption for IPTC record 2:125
+
+                                        case "2:130":   // Application Record:Image Type
+                                                // Lookup Number of Components
+                                                if ( $IPTC_Record['RecData']{0} == "0" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">No Objectdata";
+                                                }
+                                                elseif ( $IPTC_Record['RecData']{0} == "9" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Supplemental objects related to other objectdata";
+                                                }
+                                                else
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Number of Colour Components : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{0} ) );
+                                                }
+
+                                                // Lookup current objectdata colour
+                                                if ( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] == "" )
+                                                {
+                                                        $output_str .= ", Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData']{1} ) );
+                                                }
+                                                else
+                                                {
+                                                        $output_str .= ", " . nl2br( HTML_UTF8_Escape( $GLOBALS['ImageType_Names'][ $IPTC_Record['RecData']{1} ] ) );
+                                                }
+                                                $output_str .= "</td></tr>\n";
+                                                break;
+
+                                        case "2:131":   // Application Record:Image Orientation
+                                                // Lookup value
+                                                if ( $IPTC_Record['RecData'] == "L" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Landscape</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "P" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Portrait</td></tr>\n";
+                                                }
+                                                elseif ( $IPTC_Record['RecData'] == "S" )
+                                                {
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Square</td></tr>\n";
+                                                }
+                                                else
+                                                {
+                                                        // Unknown Orientation Value
+                                                        $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">Unknown : " . nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                }
+                                                break;
+
+                                        default:        // All other records
+                                                $output_str .= "<tr class=\"IPTC_Table_Row\"><td class=\"IPTC_Caption_Cell\">$Record_Name</td><td class=\"IPTC_Value_Cell\">" .nl2br( HTML_UTF8_Escape( $IPTC_Record['RecData'] ) ) ."</td></tr>\n";
+                                                break;
+                                }
+                        }
+                }
+
+                // Add Table End to HTML
+                $output_str .= "</table><br>\n";
+        }
+
+        // Return HTML
+        return $output_str;
+}
+
+
+/******************************************************************************
+* End of Function:     Interpret_IPTC_to_HTML
+******************************************************************************/
+
+
+
+/******************************************************************************
+* Global Variable:      IPTC_Entry_Names
+*
+* Contents:     The names of the IPTC-NAA IIM fields
+*
+******************************************************************************/
+
+$GLOBALS[ "IPTC_Entry_Names" ] = array(
+// Envelope Record
+"1:00" => "Model Version",
+"1:05" => "Destination",
+"1:20" => "File Format",
+"1:22" => "File Format Version",
+"1:30" => "Service Identifier",
+"1:40" => "Envelope Number",
+"1:50" => "Product ID",
+"1:60" => "Envelope Priority",
+"1:70" => "Date Sent",
+"1:80" => "Time Sent",
+"1:90" => "Coded Character Set",
+"1:100" => "UNO (Unique Name of Object)",
+"1:120" => "ARM Identifier",
+"1:122" => "ARM Version",
+
+// Application Record
+"2:00" => "Record Version",
+"2:03" => "Object Type Reference",
+"2:05" => "Object Name (Title)",
+"2:07" => "Edit Status",
+"2:08" => "Editorial Update",
+"2:10" => "Urgency",
+"2:12" => "Subject Reference",
+"2:15" => "Category",
+"2:20" => "Supplemental Category",
+"2:22" => "Fixture Identifier",
+"2:25" => "Keywords",
+"2:26" => "Content Location Code",
+"2:27" => "Content Location Name",
+"2:30" => "Release Date",
+"2:35" => "Release Time",
+"2:37" => "Expiration Date",
+"2:35" => "Expiration Time",
+"2:40" => "Special Instructions",
+"2:42" => "Action Advised",
+"2:45" => "Reference Service",
+"2:47" => "Reference Date",
+"2:50" => "Reference Number",
+"2:55" => "Date Created",
+"2:60" => "Time Created",
+"2:62" => "Digital Creation Date",
+"2:63" => "Digital Creation Time",
+"2:65" => "Originating Program",
+"2:70" => "Program Version",
+"2:75" => "Object Cycle",
+"2:80" => "By-Line (Author)",
+"2:85" => "By-Line Title (Author Position) [Not used in Photoshop 7]",
+"2:90" => "City",
+"2:92" => "Sub-Location",
+"2:95" => "Province/State",
+"2:100" => "Country/Primary Location Code",
+"2:101" => "Country/Primary Location Name",
+"2:103" => "Original Transmission Reference",
+"2:105" => "Headline",
+"2:110" => "Credit",
+"2:115" => "Source",
+"2:116" => "Copyright Notice",
+"2:118" => "Contact",
+"2:120" => "Caption/Abstract",
+"2:122" => "Caption Writer/Editor",
+"2:125" => "Rasterized Caption",
+"2:130" => "Image Type",
+"2:131" => "Image Orientation",
+"2:135" => "Language Identifier",
+"2:150" => "Audio Type",
+"2:151" => "Audio Sampling Rate",
+"2:152" => "Audio Sampling Resolution",
+"2:153" => "Audio Duration",
+"2:154" => "Audio Outcue",
+"2:200" => "ObjectData Preview File Format",
+"2:201" => "ObjectData Preview File Format Version",
+"2:202" => "ObjectData Preview Data",
+
+// Pre-ObjectData Descriptor Record
+"7:10"  => "Size Mode",
+"7:20"  => "Max Subfile Size",
+"7:90"  => "ObjectData Size Announced",
+"7:95"  => "Maximum ObjectData Size",
+
+// ObjectData Record
+"8:10"  => "Subfile",
+
+// Post ObjectData Descriptor Record
+"9:10"  => "Confirmed ObjectData Size"
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IPTC_Entry_Names
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      IPTC_Entry_Descriptions
+*
+* Contents:     The Descriptions of the IPTC-NAA IIM fields
+*
+******************************************************************************/
+
+$GLOBALS[ "IPTC_Entry_Descriptions" ] = array(
+// Envelope Record
+"1:00" => "2 byte binary version number",
+"1:05" => "Max 1024 characters of Destination",
+"1:20" => "2 byte binary file format number, see IPTC-NAA V4 Appendix A",
+"1:22" => "Binary version number of file format",
+"1:30" => "Max 10 characters of Service Identifier",
+"1:40" => "8 Character Envelope Number",
+"1:50" => "Product ID - Max 32 characters",
+"1:60" => "Envelope Priority - 1 numeric characters",
+"1:70" => "Date Sent - 8 numeric characters CCYYMMDD",
+"1:80" => "Time Sent - 11 characters HHMMSS±HHMM",
+"1:90" => "Coded Character Set - Max 32 characters",
+"1:100" => "UNO (Unique Name of Object) - 14 to 80 characters",
+"1:120" => "ARM Identifier - 2 byte binary number",
+"1:122" => "ARM Version - 2 byte binary number",
+
+// Application Record
+"2:00" => "Record Version - 2 byte binary number",
+"2:03" => "Object Type Reference -  3 plus 0 to 64 Characters",
+"2:05" => "Object Name (Title) - Max 64 characters",
+"2:07" => "Edit Status - Max 64 characters",
+"2:08" => "Editorial Update - 2 numeric characters",
+"2:10" => "Urgency - 1 numeric character",
+"2:12" => "Subject Reference - 13 to 236 characters",
+"2:15" => "Category - Max 3 characters",
+"2:20" => "Supplemental Category - Max 32 characters",
+"2:22" => "Fixture Identifier - Max 32 characters",
+"2:25" => "Keywords - Max 64 characters",
+"2:26" => "Content Location Code - 3 characters",
+"2:27" => "Content Location Name - Max 64 characters",
+"2:30" => "Release Date - 8 numeric characters CCYYMMDD",
+"2:35" => "Release Time - 11 characters HHMMSS±HHMM",
+"2:37" => "Expiration Date - 8 numeric characters CCYYMMDD",
+"2:35" => "Expiration Time - 11 characters HHMMSS±HHMM",
+"2:40" => "Special Instructions - Max 256 Characters",
+"2:42" => "Action Advised - 2 numeric characters",
+"2:45" => "Reference Service - Max 10 characters",
+"2:47" => "Reference Date - 8 numeric characters CCYYMMDD",
+"2:50" => "Reference Number - 8 characters",
+"2:55" => "Date Created - 8 numeric characters CCYYMMDD",
+"2:60" => "Time Created - 11 characters HHMMSS±HHMM",
+"2:62" => "Digital Creation Date - 8 numeric characters CCYYMMDD",
+"2:63" => "Digital Creation Time - 11 characters HHMMSS±HHMM",
+"2:65" => "Originating Program - Max 32 characters",
+"2:70" => "Program Version - Max 10 characters",
+"2:75" => "Object Cycle - 1 character",
+"2:80" => "By-Line (Author) - Max 32 Characters",
+"2:85" => "By-Line Title (Author Position) - Max 32 characters",
+"2:90" => "City - Max 32 Characters",
+"2:92" => "Sub-Location - Max 32 characters",
+"2:95" => "Province/State - Max 32 Characters",
+"2:100" => "Country/Primary Location Code - 3 alphabetic characters",
+"2:101" => "Country/Primary Location Name - Max 64 characters",
+"2:103" => "Original Transmission Reference - Max 32 characters",
+"2:105" => "Headline - Max 256 Characters",
+"2:110" => "Credit - Max 32 Characters",
+"2:115" => "Source - Max 32 Characters",
+"2:116" => "Copyright Notice - Max 128 Characters",
+"2:118" => "Contact - Max 128 characters",
+"2:120" => "Caption/Abstract - Max 2000 Characters",
+"2:122" => "Caption Writer/Editor - Max 32 Characters",
+"2:125" => "Rasterized Caption - 7360 bytes, 1 bit per pixel, 460x128pixel image",
+"2:130" => "Image Type - 2 characters",
+"2:131" => "Image Orientation - 1 alphabetic character",
+"2:135" => "Language Identifier - 2 or 3 aphabetic characters",
+"2:150" => "Audio Type - 2 characters",
+"2:151" => "Audio Sampling Rate - 6 numeric characters",
+"2:152" => "Audio Sampling Resolution - 2 numeric characters",
+"2:153" => "Audio Duration - 6 numeric characters",
+"2:154" => "Audio Outcue - Max 64 characters",
+"2:200" => "ObjectData Preview File Format - 2 byte binary number",
+"2:201" => "ObjectData Preview File Format Version - 2 byte binary number",
+"2:202" => "ObjectData Preview Data - Max 256000 binary bytes",
+
+// Pre-ObjectData Descriptor Record
+"7:10"  => "Size Mode - 1 numeric character",
+"7:20"  => "Max Subfile Size",
+"7:90"  => "ObjectData Size Announced",
+"7:95"  => "Maximum ObjectData Size",
+
+// ObjectData Record
+"8:10"  => "Subfile",
+
+// Post ObjectData Descriptor Record
+"9:10"  => "Confirmed ObjectData Size"
+
+);
+
+/******************************************************************************
+* End of Global Variable:     IPTC_Entry_Descriptions
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+* Global Variable:      IPTC_File Formats
+*
+* Contents:     The names of the IPTC-NAA IIM File Formats for field 1:20
+*
+******************************************************************************/
+
+$GLOBALS[ "IPTC_File Formats" ] = array(
+00 => "No ObjectData",
+01 => "IPTC-NAA Digital Newsphoto Parameter Record",
+02 => "IPTC7901 Recommended Message Format",
+03 => "Tagged Image File Format (Adobe/Aldus Image data)",
+04 => "Illustrator (Adobe Graphics data)",
+05 => "AppleSingle (Apple Computer Inc)",
+06 => "NAA 89-3 (ANPA 1312)",
+07 => "MacBinary II",
+08 => "IPTC Unstructured Character Oriented File Format (UCOFF)",
+09 => "United Press International ANPA 1312 variant",
+10 => "United Press International Down-Load Message",
+11 => "JPEG File Interchange (JFIF)",
+12 => "Photo-CD Image-Pac (Eastman Kodak)",
+13 => "Microsoft Bit Mapped Graphics File [*.BMP]",
+14 => "Digital Audio File [*.WAV] (Microsoft & Creative Labs)",
+15 => "Audio plus Moving Video [*.AVI] (Microsoft)",
+16 => "PC DOS/Windows Executable Files [*.COM][*.EXE]",
+17 => "Compressed Binary File [*.ZIP] (PKWare Inc)",
+18 => "Audio Interchange File Format AIFF (Apple Computer Inc)",
+19 => "RIFF Wave (Microsoft Corporation)",
+20 => "Freehand (Macromedia/Aldus)",
+21 => "Hypertext Markup Language - HTML (The Internet Society)",
+22 => "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC",
+23 => "MPEG 2 Audio Layer 3, ISO/IEC",
+24 => "Portable Document File (*.PDF) Adobe",
+25 => "News Industry Text Format (NITF)",
+26 => "Tape Archive (*.TAR)",
+27 => "Tidningarnas Telegrambyrĺ NITF version (TTNITF DTD)",
+28 => "Ritzaus Bureau NITF version (RBNITF DTD)",
+29 => "Corel Draw [*.CDR]"
+);
+
+
+/******************************************************************************
+* End of Global Variable:     IPTC_File Formats
+******************************************************************************/
+
+/******************************************************************************
+* Global Variable:      ImageType_Names
+*
+* Contents:     The names of the colour components for IPTC-NAA IIM field 2:130
+*
+******************************************************************************/
+
+$GLOBALS['ImageType_Names'] = array(    "M" => "Monochrome",
+                                        "Y" => "Yellow Component",
+                                        "M" => "Magenta Component",
+                                        "C" => "Cyan Component",
+                                        "K" => "Black Component",
+                                        "R" => "Red Component",
+                                        "G" => "Green Component",
+                                        "B" => "Blue Component",
+                                        "T" => "Text Only",
+                                        "F" => "Full colour composite, frame sequential",
+                                        "L" => "Full colour composite, line sequential",
+                                        "P" => "Full colour composite, pixel sequential",
+                                        "S" => "Full colour composite, special interleaving" );
+
+
+
+/******************************************************************************
+* End of Global Variable:     ImageType_Names
+******************************************************************************/
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Edit_File_Info_Example.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info_Example.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info_Example.php	2011/08/17 20:08:53	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Edit_File_Info_Example.php	2005/01/20 10:32:52
@@ -0,0 +1,248 @@
+<html>
+
+<!--***************************************************************************
+*
+* Filename:     Edit_File_Info_Example.php
+*
+* Description:  An example file showing how edit_file_info allows the user to edit
+*               the metadata of an image over the internet in the same way
+*               that Photoshop edits 'File Info' data
+*
+* Author:       Evan Hunter
+*
+* Date:         17/11/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.10 -> 1.11 : Changed displayed toolkit version numbers to reference Toolkit_Version.php
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+***************************************************************************-->
+
+        <head>
+
+                <META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">
+                <STYLE TYPE="text/css" MEDIA="screen, print, projection">
+                <!--
+
+                        BODY { background-color:#505050; color:#F0F0F0 }
+                        a  { color:orange  }
+                        .EXIF_Main_Heading { color:red }
+                        .EXIF_Secondary_Heading{ color: orange}
+                        .EXIF_Table {  border-collapse: collapse ; border: 1px solid #909000}
+                        .EXIF_Table tbody td{border-width: 1px; border-style:solid; border-color: #909000;}
+
+                -->
+                </STYLE>
+
+
+                <?php
+                        // Turn off Error Reporting
+                        error_reporting ( 0 );
+
+                        include 'Toolkit_Version.php';          // Change: added as of version 1.11
+
+                        // Retrieve the JPEG image filename from the http url request
+                        if ( ( !array_key_exists( 'jpeg_fname', $GLOBALS['HTTP_GET_VARS'] ) ) ||
+                             ( $GLOBALS['HTTP_GET_VARS']['jpeg_fname'] == "" ) )
+                        {
+                                echo "<title>No image filename defined</title>\n";
+                                echo "</head>\n";
+                                echo "<body>\n";
+                                echo "<p>No image filename defined - use GET method with field: jpeg_fname</p>\n";
+                                echo "<p><a href=\"http://www.ozhiker.com/electronics/pjmt/\" >PHP JPEG Metadata Toolkit version " . $GLOBALS['Toolkit_Version'] . ", Copyright (C) 2004 Evan Hunter</a></p>\n";         // Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11
+                                echo "</body>\n";
+                                exit( );
+
+                        }
+                        else
+                        {
+                                $filename = $GLOBALS['HTTP_GET_VARS']['jpeg_fname'];
+                        }
+                 ?>
+
+
+                <title>Edit Photoshop File Info details for <?php $filename ?></title>
+        </head>
+
+        <body >
+                <p>Powered by: <a href="http://www.ozhiker.com/electronics/pjmt/" >PHP JPEG Metadata Toolkit version <?php echo $GLOBALS['Toolkit_Version'] ?>, Copyright (C) 2004 Evan Hunter</a></p>    <!-- Change: displayed toolkit version numbers to reference Toolkit_Version.php - as of version 1.11 -->
+                <br>
+                <br>
+
+
+                <?php
+                        // Output a heading
+                        echo "<H1>Edit Photoshop File Info details for $filename</H1>";
+
+                        // Output a link to display the full metadata
+                        echo "<p><a href=\"Example.php?jpeg_fname=" . $filename . "\" >View Full Metatdata Information</a></p>\n";
+
+
+                        // Display a small copy of the image
+                        echo "<p><img src=\"$filename\" height=\"50%\"></p>";
+
+                        // Define defaults for the fields - These are only used where the image has blank fields
+                        $default_ps_file_info_array = array (
+                                                                'title'                 => "",
+                                                                'author'                => "Evan Hunter",
+                                                                'authorsposition'       => "",
+                                                                'caption'               => "",
+                                                                'captionwriter'         => "Evan Hunter",
+                                                                'jobname'               => "",
+                                                                'copyrightstatus'       => "Copyrighted Work",
+                                                                'copyrightnotice'       => "Copyright (c) Evan Hunter 2004",
+                                                                'ownerurl'              => "http://www.ozhiker.com",
+                                                                'keywords'              => array(),
+                                                                'category'              => "",
+                                                                'supplementalcategories'=> array(),
+                                                                'date'                  => "",
+                                                                'city'                  => "",
+                                                                'state'                 => "Tasmania",
+                                                                'country'               => "Australia",
+                                                                'credit'                => "Evan Hunter",
+                                                                'source'                => "Evan Hunter",
+                                                                'headline'              => "",
+                                                                'instructions'          => "",
+                                                                'transmissionreference' => "",
+                                                                'urgency'               => ""
+                                                                );
+
+                        // outputfilename must always be defined, as it specifies the
+                        // file which will be changed
+
+                        // These two lines create a temporary copy of the file
+                        // which will be the one that is edited, keeping
+                        // the original intact. - This would not be required if you wanted
+                        // to change the original - in that case just set $outputfilename = $filename
+                        $outputfilename = get_next_filename( );
+                        copy( $filename, $outputfilename );
+
+
+
+
+                        // Include the File Info Editor.
+
+                        include "Edit_File_Info.php";
+
+                ?>
+
+
+        </body>
+
+</html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<?php
+
+/******************************************************************************
+*
+* Function:     get_next_filename
+*
+* Description:  Simple function to cycle through temporary filenames ( a to z )
+*               This means that there will only be a maximum of 26 temporary files,
+*               hence avoiding filling up the server or having a cron job to remove them.
+*
+*               NOTE: This function would not normally be required, and is just
+*                     to protect my website (and others) from filling up with
+*                     temporary files whilst demonstrating the toolkit
+*
+* Parameters:   none
+*
+* Returns:      TRUE - on Success
+*               FALSE - on Failure
+*
+******************************************************************************/
+
+function get_next_filename( )
+{
+        // Read the letter of the next temp file from disk
+        $filename = file( "next_temp_file.dat" );
+        // If it wasn't read - start at 'a'
+        if ( $filename == FALSE )
+        {
+                $filename = 'a';
+        }
+        else
+        {
+                $filename = $filename{0};
+        }
+
+        // Ensure the filename letter is valid
+        if ( ( $filename < 'a' ) || ( $filename > 'z' ) )
+        {
+                $filename = 'a';
+        }
+
+
+        // Check if the names are up to 'z'
+        if( $filename == 'z' )
+        {
+                // Name is at z - the next one should be 'a'
+                $new_filename = 'a';
+        }
+        else
+        {
+                // The name is not 'z' add one to it to get the next value
+                $new_filename = chr( ord( $filename ) + 1 );
+        }
+
+        // Write the next temp file letter back into the file
+        $Fhnd = fopen ("next_temp_file.dat", "w");
+        fwrite ($Fhnd, $new_filename);
+        fclose ($Fhnd);
+
+        // return the filename
+        return "temp_$filename.jpg";
+}
+
+/******************************************************************************
+* End of Function:     get_next_filename
+******************************************************************************/
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/XML.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/XML.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/XML.php	2011/08/17 20:09:02	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/XML.php	2004/11/22 21:26:56
@@ -0,0 +1,396 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     XML.php
+*
+* Description:  Provides functions for parsing and constructing XML information
+*
+* Author:       Evan Hunter
+*
+* Date:         27/7/2004
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.10
+*
+* Changes:      1.00 -> 1.10 : Changed read_xml_array_from_text to fix problem that
+*                              caused the whitespace (especially newlines) to be
+*                              destroyed when converting xml text to an xml array
+*
+* URL:          http://electronics.ozhiker.com
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+include_once 'Unicode.php';          // Unicode is required as XML is always Unicode encoded
+
+
+/******************************************************************************
+*
+* Function:     read_xml_array_from_text
+*
+* Description:  Parses a string containing XML, and returns the resulting
+*               tree structure array, which contains all the XML information.
+*               Note: White space and comments in the XML are ignored
+*               Note: All text information contained in the tree structure
+*                     is encoded as Unicode UTF-8. Hence text will appear as
+*                     normal ASCII except where there is an extended character.
+*
+* Parameters:   xmltext - a string containing the XML to be parsed
+*
+* Returns:      output - the tree structure array containing the XML information
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function read_xml_array_from_text( $xmltext )
+{
+        // Check if there actually is any text to parse
+        if ( trim( $xmltext ) == "" )
+        {
+                return FALSE;
+        }
+
+        // Create an instance of a xml parser to parse the XML text
+        $xml_parser = xml_parser_create( "UTF-8" );
+
+
+        // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10
+
+        // We would like to remove unneccessary white space, but this will also
+        // remove things like newlines (&#xA;) in the XML values, so white space
+        // will have to be removed later
+        if ( xml_parser_set_option($xml_parser,XML_OPTION_SKIP_WHITE,0) == FALSE )
+        {
+                // Error setting case folding - destroy the parser and return
+                xml_parser_free($xml_parser);
+                return FALSE;
+        }
+
+        // to use XML code correctly we have to turn case folding
+        // (uppercasing) off. XML is case sensitive and upper
+        // casing is in reality XML standards violation
+        if ( xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0) == FALSE )
+        {
+                // Error setting case folding - destroy the parser and return
+                xml_parser_free($xml_parser);
+                return FALSE;
+        }
+
+        // Parse the XML text into a array structure
+        if ( xml_parse_into_struct($xml_parser, $xmltext, $vals, $index) == 0 )
+        {
+                // Error Parsing XML - destroy the parser and return
+                xml_parser_free($xml_parser);
+                return FALSE;
+        }
+
+        // Destroy the xml parser
+        xml_parser_free($xml_parser);
+
+
+        // Change: Fixed problem that caused the whitespace (especially newlines) to be destroyed when converting xml text to an xml array, as of revision 1.10
+
+        // Since the xml was processed with whitespace enabled, it will have many values which are
+        // only whitespace. These need to be removed to make a sensible array.
+
+        $newvals = array( );
+
+        // Cycle through each of the items
+        foreach( $vals as $valno => $val )
+        {
+                // If the item has a whitespace only value, remove it
+                if ( ( array_key_exists( 'value', $val ) ) && (trim( $val[ 'value' ] ) == "" ) )
+                {
+                        unset( $val[ 'value' ] );
+                }
+                // If the item has a value (which will be non blank now) or is of type other than cdata, add it to the new array
+                if ( ( $val[ 'type' ] != 'cdata' ) || ( array_key_exists( 'value', $val ) ) )
+                {
+                        $newvals[] = $val;
+                }
+
+        }
+
+        // The xml_parse_into_struct function returns a flat version
+        // of the XML data, where each tag has a level number attached.
+        // This is very difficult to work with, so it needs to be
+        // converted to a tree structure before being returned
+        return xml_get_children($newvals, $i=0);
+
+}
+
+/******************************************************************************
+* End of Function:     read_xml_array_from_text
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     write_xml_array_to_text
+*
+* Description:  Takes a tree structure array (in the same format as returned
+*               by read_xml_array_from_text, and constructs a string containing
+*               the equivalent XML. This function is recursive, and produces
+*               XML which has correct indents.
+*               Note: All text information contained in the tree structure
+*                     can be either 7-bit ASCII or encoded as Unicode UTF-8,
+*                     since UTF-8 passes 7-bit ASCII text unchanged.
+*
+* Parameters:   xmlarray - the tree structure array containing the information to
+*                          be converted to XML
+*               indentlevel - the indent level of the top level tags (usually zero)
+*
+* Returns:      output - the string containing the equivalent XML
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function write_xml_array_to_text( $xmlarray, $indentlevel )
+{
+        // Create a string to receive the XML
+        $output_xml_text = "";
+
+
+        // Cycle through each xml element at this level
+        foreach ($xmlarray as $xml_elem)
+        {
+
+                // Add the indent, then the cleaned tag name to the output
+                $output_xml_text .= str_repeat ( " ", $indentlevel ) . "<" . xml_UTF8_clean( $xml_elem['tag'] );
+
+                // Check if there are any attributes for this tag
+                if (array_key_exists('attributes',$xml_elem))
+                {
+                        // There are attributes
+                        // Cycle through each attribute for this tag
+                        foreach ($xml_elem['attributes'] as  $xml_attr_name => $xml_attr_val)
+                        {
+                                // Add the cleaned attribute name, and cleaned attribute value to the output
+                                $output_xml_text .= " ". xml_UTF8_clean( $xml_attr_name ) ." ='" .  xml_UTF8_clean( $xml_attr_val ) ."'";
+                        }
+                }
+
+                // Add the 'greater-than' to close this tag to the output
+                $output_xml_text .= ">";
+
+                // Check if this element has any text inside it.
+                if (array_key_exists('value',$xml_elem) )
+                {
+                        // There is text for this element - clean it and add it to the output
+                        $output_xml_text .=  xml_UTF8_clean( $xml_elem['value'] );
+                }
+
+                // Check if there are any lower levels contained by this element
+                if (array_key_exists('children',$xml_elem) )
+                {
+                        // There are sub-elements for this element
+
+                        // Add a newline to the output, so the sub-elements start on a fresh line
+                        $output_xml_text .= "\n";
+
+                        // Recursively call this function to output the sub-elements, and add the result to the output
+                        $output_xml_text .= write_xml_array_to_text( $xml_elem['children'], $indentlevel + 1 );
+
+                        // Add an indent to the output for the closing tag, since we are on a new line due to the sub-elements
+                        $output_xml_text .= str_repeat ( " ", $indentlevel );
+                }
+
+                // Add the cleaned closing tag to the output
+                $output_xml_text .= "</" .xml_UTF8_clean($xml_elem['tag']) . ">\n";
+        }
+
+        // Return the XML text
+        return $output_xml_text;
+}
+
+/******************************************************************************
+* End of Function:     write_xml_array_to_text
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+*         INTERNAL FUNCTIONS
+*
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     xml_get_children
+*
+* Description:  Used by the read_xml_array_from_text function.
+*               This function recursively converts the values retrieved from
+*               the xml_parse_into_struct function into a tree structure array,
+*               which is much more useful and easier to use.
+*
+* Parameters:   input_xml_array - the flat array of XML elements retrieved
+*                                 from xml_parse_into_struct
+*               $item_num - the number of the element at which the conversion
+*                           should start (usually zero when called from another
+*                           function, this is used for recursion)
+*
+* Returns:      children - the tree structure array containing XML elements
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function xml_get_children( &$input_xml_array, &$item_num )
+{
+
+        // Make an array to receive the output XML tree structure
+        $children = array();
+
+
+        // Cycle through all the elements of the input XML array
+        while ( $item_num < count( $input_xml_array ) )
+        {
+                // Retrieve the current array element
+                $v = &$input_xml_array[ $item_num++ ];
+
+                // Check what type of XML array element this is, and process accordingly
+
+                switch ( $v['type'] )
+                {
+                        case 'cdata':     // This is a non parsed Character Data tag
+                        case 'complete':  // This is a pair of XML matching tags possibly with text (but no tags) inside
+                                $children[] = xml_get_child( $v );
+                                break;
+
+                        case 'open':      // This is a single opening tag
+                                // Recursively get the children for this opening tag
+                                $children[] = xml_get_child( $v, xml_get_children( $input_xml_array, $item_num ) );
+                                break;    // This is a single opening tag
+
+                        case 'close':     // This is a single closing tag
+                                break 2;  // leave "while" loop (and the function)
+                }
+        }
+
+        // Return the results
+        return $children;
+}
+
+/******************************************************************************
+* End of Function:     xml_get_children
+******************************************************************************/
+
+
+/******************************************************************************
+*
+* Internal Function:     xml_get_child
+*
+* Description:  Used by the xml_get_children function.
+*               Takes an element from an array provided by xml_parse_into_struct
+*               and returns an element for a tree structure array
+*
+* Parameters:   input_xml_item - the item from the array provided by xml_parse_into_struct
+*               children - an array of sub-elements to be added to the tree
+*                          structure array. Null or missing value indicate no
+*                          sub-elements are to be added.
+*
+* Returns:      child - the element for a tree structure array
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function xml_get_child( &$input_xml_item, $children = NULL )
+{
+        // Create an array to receive the child structure
+        $child = array();
+
+        // If the input item has the 'tag' element set, copy it to the child
+        if ( isset( $input_xml_item['tag'] ) )
+        {
+                $child['tag'] = $input_xml_item['tag'] ;
+        }
+
+        // If the input item has the 'value' element set, copy it to the child
+        if ( isset( $input_xml_item['value'] ) )
+        {
+                $child['value'] = $input_xml_item['value'] ;
+        }
+
+        // If the input item has the 'attributes' element set, copy it to the child
+        if ( isset( $input_xml_item['attributes'] ) )
+        {
+                $child['attributes'] = $input_xml_item['attributes'];
+        }
+
+        // If children have been specified, add them to the child
+        if ( is_array( $children ) )
+        {
+                $child['children'] = $children;
+        }
+
+        // Return the child structure
+        return $child;
+}
+
+/******************************************************************************
+* End of Function:     xml_get_children
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/pjmt_utils.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/pjmt_utils.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/pjmt_utils.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/pjmt_utils.php	2005/01/20 00:51:44
@@ -0,0 +1,150 @@
+<?php
+/******************************************************************************
+*
+* Filename:     pjmt_utils.php
+*
+* Description:  Provides various utility functions for the toolkit
+*
+* Author:       Evan Hunter
+*
+* Date:         20/1/2005
+*
+* Project:      JPEG Metadata
+*
+* Revision:     1.11
+*
+* NOTE:         This file will change with every revision update, hence will not
+*               be shown in the changes list
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+/******************************************************************************
+*
+* Function:     get_relative_path
+*
+* Description:  Creates a relative path version of a file or directiroy name,
+*               given a directory that it will be relative to.
+*
+* Parameters:   target - a file or directory name which will be made relative
+*               fromdir - the directory which the returned path is relative to
+*
+* Returns:      output - the relative path
+*
+******************************************************************************/
+
+function get_relative_path( $target, $fromdir )
+{
+        // Check that the fromdir has a trailing slash, otherwise realpath will
+        // strip the last directory name off
+        if ( ( $fromdir[ strlen( $fromdir ) - 1 ] != "\\" ) &&
+             ( $fromdir[ strlen( $fromdir ) - 1 ] != "/" ) )
+        {
+                $fromdir .= "/";
+        }
+
+        // get a real directory name for each of the target and from directory
+        $from = realpath( $fromdir );
+        $target = realpath( $target );
+        $to = dirname( $target  );
+
+        // Can't get relative path with drive in path - remove it
+        if ( ( $colonpos = strpos( $target, ":" ) ) != FALSE )
+        {
+                $target = substr( $target, $colonpos+1 );
+        }
+        if ( ( $colonpos = strpos( $from, ":" ) ) != FALSE )
+        {
+                $from = substr( $from, $colonpos+1 );
+        }
+        if ( ( $colonpos = strpos( $to, ":" ) ) != FALSE )
+        {
+                $to = substr( $to, $colonpos+1 );
+        }
+
+
+        $path = "../";
+        $posval = 0;
+        // Step through the paths until a difference is found (ignore slash, backslash differences
+        // or the end of one is found
+        while ( ( ( $from[$posval] == $to[$posval] ) ||
+                  ( ( $from[$posval] == "\\" ) && ( $to[$posval] == "/" ) ) ||
+                  ( ( $from[$posval] == "/" ) && ( $to[$posval] == "\\" ) ) ) &&
+                ( $from[$posval] && $to[$posval] ) )
+        {
+                $posval++;
+        }
+        // Save the position of the first difference
+        $diffpos = $posval;
+
+        // Check if the directories are the same or
+        // the if target is in a subdirectory of the fromdir
+        if ( ( ! $from[$posval] ) &&
+             ( $to[$posval] == "/" || $to[$posval] == "\\" || !$to[$posval] ) )
+        {
+                // target is in fromdir or a subdirectory
+                // Build relative path starting with a ./
+                return ( "./" . substr( $target, $posval+1, strlen( $target ) ) );
+        }
+        else
+        {
+                // target is outside the fromdir branch
+                // find out how many "../"'s are necessary
+                // Step through the fromdir path, checking for slashes
+                // each slash encountered requires a "../"
+                while ( $from[++$posval] )
+                {
+                        // Check for slash
+                        if ( ( $from[$posval] == "/" ) || ( $from[$posval] == "\\" ) )
+                        {
+                                // Found a slash, add a "../"
+                                $path .= "../";
+                        }
+                }
+
+                // Search backwards to find where the first common directory
+                // as some letters in the first different directory names
+                // may have been the same
+                $diffpos--;
+                while ( ( $to[$diffpos] != "/" ) && ( $to[$diffpos] != "\\" ) && $to[$diffpos] )
+                {
+                        $diffpos--;
+                }
+                // Build relative path to return
+
+                return ( $path . substr( $target, $diffpos+1, strlen( $target ) ) );
+        }
+}
+
+/******************************************************************************
+* End of Function:     get_relative_path
+******************************************************************************/
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/Photoshop_IRB.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_IRB.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_IRB.php	2011/08/17 20:09:01	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/Photoshop_IRB.php	2005/01/20 10:45:02
@@ -0,0 +1,1514 @@
+<?
+
+/******************************************************************************
+*
+* Filename:     Photoshop_IRB.php
+*
+* Description:  Provides functions for reading and writing information to/from
+*               the 'App 13' Photoshop Information Resource Block segment of
+*               JPEG format files
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.02 : changed get_Photoshop_IRB to work with corrupted
+*                              resource names which Photoshop can still read
+*               1.02 -> 1.03 : Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00"
+*                              string with every APP13 segment, not just the first one
+*               1.03 -> 1.10 : changed get_Photoshop_IRB to fix processing of embedded resource names,
+*                              after discovering that Photoshop does not process
+*                              resource names according to the standard :
+*                              "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
+*                              This is an update to the change 1.00 -> 1.02, which was not fully correct
+*                              changed put_Photoshop_IRB to fix the writing of embedded resource name,
+*                              to avoid creating blank resources, and to fix a problem
+*                              causing the IRB block to be incorrectly positioned if no APP segments existed.
+*                              changed get_Photoshop_IPTC to initialise the output array correctly.
+*               1.10 -> 1.11 : Moved code out of get_Photoshop_IRB into new function unpack_Photoshop_IRB_Data
+*                              to allow reading of IRB blocks embedded within EXIF (for TIFF Files)
+*                              Moved code out of put_Photoshop_IRB into new function pack_Photoshop_IRB_Data
+*                              to allow writing of IRB blocks embedded within EXIF (for TIFF Files)
+*                              Enabled the usage of $GLOBALS['HIDE_UNKNOWN_TAGS'] to hide unknown resources
+*                              changed Interpret_IRB_to_HTML to allow thumbnail links to work when
+*                              toolkit is portable across directories
+*
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+// Change: as of version 1.11 - added to ensure the HIDE_UNKNOWN_TAGS variable is set even if EXIF.php is not included
+if ( !isset( $GLOBALS['HIDE_UNKNOWN_TAGS'] ) )     $GLOBALS['HIDE_UNKNOWN_TAGS']= FALSE;
+
+include_once 'IPTC.php';
+include_once 'Unicode.php';
+
+
+
+// TODO: Many Photoshop IRB resources not interpeted
+// TODO: Obtain a copy of the Photoshop CS File Format Specification
+// TODO: Find out what Photoshop IRB resources 1061, 1062 & 1064 are
+// TODO: Test get_Photoshop_IRB and put_Photoshop_IRB with multiple APP13 segments
+
+/******************************************************************************
+*
+* Function:     get_Photoshop_IRB
+*
+* Description:  Retrieves the Photoshop Information Resource Block (IRB) information
+*               from an App13 JPEG segment and returns it as an array. This may
+*               include IPTC-NAA IIM Information. Uses information
+*               supplied by the get_jpeg_header_data function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      IRBdata - The array of Photoshop IRB records
+*               FALSE - if an APP 13 Photoshop IRB segment could not be found,
+*                       or if an error occured
+*
+******************************************************************************/
+
+function get_Photoshop_IRB( $jpeg_header_data )
+{
+        // Photoshop Image Resource blocks can span several JPEG APP13 segments, so we need to join them up if there are more than one
+        $joined_IRB = "";
+
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP13 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 )
+                {
+                        // And if it has the photoshop label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 )
+                        {
+                                // join it to the other previous IRB data
+                                $joined_IRB .= substr ( $jpeg_header_data[$i]['SegData'], 14 );
+                        }
+                }
+        }
+
+        // If there was some Photoshop IRB information found,
+        if ( $joined_IRB != "" )
+        {
+                // Found a Photoshop Image Resource Block - extract it.
+                // Change: Moved code into unpack_Photoshop_IRB_Data to allow TIFF reading as of 1.11
+                return unpack_Photoshop_IRB_Data( $joined_IRB );
+
+        }
+        else
+        {
+                // No Photoshop IRB found
+                return FALSE;
+        }
+
+}
+
+/******************************************************************************
+* End of Function:     get_Photoshop_IRB
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_Photoshop_IRB
+*
+* Description:  Adds or modifies the Photoshop Information Resource Block (IRB)
+*               information from an App13 JPEG segment. If a Photoshop IRB already
+*               exists, it is replaced, otherwise a new one is inserted, using the
+*               supplied data. Uses information supplied by the get_jpeg_header_data
+*               function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*               new_IRB_data - an array of the data to be stored in the Photoshop
+*                              IRB segment. Should be in the same format as received
+*                              from get_Photoshop_IRB
+*
+* Returns:      jpeg_header_data - the JPEG header data array with the
+*                                  Photoshop IRB added.
+*               FALSE - if an error occured
+*
+******************************************************************************/
+
+function put_Photoshop_IRB( $jpeg_header_data, $new_IRB_data )
+{
+        // Delete all existing Photoshop IRB blocks - the new one will replace them
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ) ; $i++ )
+        {
+                // If we find an APP13 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP13" ) == 0 )
+                {
+                        // And if it has the photoshop label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "Photoshop 3.0\x00", 14) == 0 )
+                        {
+                                // Delete the block information - it needs to be rebuilt
+                                array_splice( $jpeg_header_data, $i, 1 );
+                        }
+                }
+        }
+
+
+        // Now we have deleted the pre-existing blocks
+
+
+        // Retrieve the Packed Photoshop IRB Data
+        // Change: Moved code into pack_Photoshop_IRB_Data to allow TIFF writing as of 1.11
+        $packed_IRB_data = pack_Photoshop_IRB_Data( $new_IRB_data );
+
+        // Change : This section changed to fix incorrect positioning of IRB segment, as of revision 1.10
+        //          when there are no APP segments present
+
+        //Cycle through the header segments in reverse order (to find where to put the APP13 block - after any APP0 to APP12 blocks)
+        $i = count( $jpeg_header_data ) - 1;
+        while (( $i >= 0 ) && ( ( $jpeg_header_data[$i]['SegType'] > 0xED ) || ( $jpeg_header_data[$i]['SegType'] < 0xE0 ) ) )
+        {
+                $i--;
+        }
+
+
+
+        // Cycle through the packed output data until it's size is less than 32000 bytes, outputting each 32000 byte block to an APP13 segment
+        while ( strlen( $packed_IRB_data ) > 32000 )
+        {
+                // Change: Fixed put_Photoshop_IRB to output "Photoshop 3.0\x00" string with every APP13 segment, not just the first one, as of 1.03
+
+                // Write a 32000 byte APP13 segment
+                array_splice($jpeg_header_data, $i +1  , 0, array(  "SegType" => 0xED,
+                                                                "SegName" => "APP13",
+                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ],
+                                                                "SegData" => "Photoshop 3.0\x00" . substr( $packed_IRB_data,0,32000) ) );
+
+                // Delete the 32000 bytes from the packed output data, that were just output
+                $packed_IRB_data = substr_replace($packed_IRB_data, '', 0, 32000);
+                $i++;
+        }
+
+        // Write the last block of packed output data to an APP13 segment - Note array_splice doesn't work with multidimensional arrays, hence inserting a blank string
+        array_splice($jpeg_header_data, $i + 1 , 0, "" );
+        $jpeg_header_data[$i + 1] =  array( "SegType" => 0xED,
+                                        "SegName" => "APP13",
+                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xED ],
+                                        "SegData" => "Photoshop 3.0\x00" . $packed_IRB_data );
+
+        return $jpeg_header_data;
+}
+
+/******************************************************************************
+* End of Function:     put_Photoshop_IRB
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_Photoshop_IPTC
+*
+* Description:  Retrieves IPTC-NAA IIM information from within a Photoshop
+*               IRB (if it is present) and returns it in an array. Uses
+*               information supplied by the get_jpeg_header_data function
+*
+* Parameters:   Photoshop_IRB_data - an array of Photoshop IRB records, as
+*                                    returned from get_Photoshop_IRB
+*
+* Returns:      IPTC_Data_Out - The array of IPTC-NAA IIM records
+*               FALSE - if an IPTC-NAA IIM record could not be found, or if
+*                       an error occured
+*
+******************************************************************************/
+
+function get_Photoshop_IPTC( $Photoshop_IRB_data )
+{
+
+        // Change: Initialise array correctly, as of revision 1.10
+        $IPTC_Data_Out = array();
+
+        //Cycle through the Photoshop 8BIM records looking for the IPTC-NAA record
+        for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ )
+        {
+                // Check if each record is a IPTC record (which has id 0x0404)
+                if ( $Photoshop_IRB_data[$i]['ResID']  == 0x0404 )
+                {
+                        // We've found an IPTC block - Decode it
+                        $IPTC_Data_Out = get_IPTC( $Photoshop_IRB_data[$i]['ResData'] );
+                }
+        }
+
+        // If there was no records put into the output array,
+        if ( count( $IPTC_Data_Out ) == 0 )
+        {
+                // Then return false
+                return FALSE;
+        }
+        else
+        {
+                // Otherwise return the array
+                return $IPTC_Data_Out;
+        }
+
+}
+/******************************************************************************
+* End of Function:     get_Photoshop_IPTC
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_Photoshop_IPTC
+*
+* Description:  Inserts a new IPTC-NAA IIM resource into a Photoshop
+*               IRB, or replaces an the existing resource if one is present.
+*               Uses information supplied by the get_Photoshop_IRB function
+*
+* Parameters:   Photoshop_IRB_data - an array of Photoshop IRB records, as
+*                                    returned from get_Photoshop_IRB, into
+*                                    which the IPTC-NAA IIM record will be inserted
+*               new_IPTC_block - an array of IPTC-NAA records in the same format
+*                                as those returned by get_Photoshop_IPTC
+*
+* Returns:      Photoshop_IRB_data - The Photoshop IRB array with the
+*                                     IPTC-NAA IIM resource inserted
+*
+******************************************************************************/
+
+function put_Photoshop_IPTC( $Photoshop_IRB_data, $new_IPTC_block )
+{
+        $iptc_block_pos = -1;
+
+        //Cycle through the 8BIM records looking for the IPTC-NAA record
+        for( $i = 0; $i < count( $Photoshop_IRB_data ); $i++ )
+        {
+                // Check if each record is a IPTC record (which has id 0x0404)
+                if ( $Photoshop_IRB_data[$i]['ResID']  == 0x0404 )
+                {
+                        // We've found an IPTC block - save the position
+                        $iptc_block_pos = $i;
+                }
+        }
+
+        // If no IPTC block was found, create a new one
+        if ( $iptc_block_pos == -1 )
+        {
+                // New block position will be at the end of the array
+                $iptc_block_pos = count( $Photoshop_IRB_data );
+        }
+
+
+        // Write the new IRB resource to the Photoshop IRB array with no data
+        $Photoshop_IRB_data[$iptc_block_pos] = array(   "ResID" =>   0x0404,
+                                                        "ResName" => $GLOBALS['Photoshop_ID_Names'][ 0x0404 ],
+                                                        "ResDesc" => $GLOBALS[ "Photoshop_ID_Descriptions" ][ 0x0404 ],
+                                                        "ResEmbeddedName" => "\x00\x00",
+                                                        "ResData" => put_IPTC( $new_IPTC_block ) );
+
+
+        // Return the modified IRB
+        return $Photoshop_IRB_data;
+}
+
+/******************************************************************************
+* End of Function:     put_Photoshop_IPTC
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_IRB_to_HTML
+*
+* Description:  Generates html showing the information contained in a Photoshop
+*               IRB data array, as retrieved with get_Photoshop_IRB, including
+*               any IPTC-NAA IIM records found.
+*
+*               Please note that the following resource numbers are not currently
+*               decoded: ( Many of these do not apply to JPEG images)
+*               0x03E9, 0x03EE, 0x03EF, 0x03F0, 0x03F1, 0x03F2, 0x03F6, 0x03F9,
+*               0x03FA, 0x03FB, 0x03FD, 0x03FE, 0x0400, 0x0401, 0x0402, 0x0405,
+*               0x040E, 0x040F, 0x0410, 0x0412, 0x0413, 0x0415, 0x0416, 0x0417,
+*               0x041B, 0x041C, 0x041D, 0x0BB7
+*
+*               ( Also these Obsolete resource numbers)
+*               0x03E8, 0x03EB, 0x03FC, 0x03FF, 0x0403
+*
+*
+* Parameters:   IRB_array - a Photoshop IRB data array as from get_Photoshop_IRB
+*               filename - the name of the JPEG file being processed ( used
+*                          by the script which displays the Photoshop thumbnail)
+*
+*
+* Returns:      output_str - the HTML string
+*
+******************************************************************************/
+
+function Interpret_IRB_to_HTML( $IRB_array, $filename )
+{
+        // Create a string to receive the HTML
+        $output_str = "";
+
+        // Check if the Photoshop IRB array is valid
+        if ( $IRB_array !== FALSE )
+        {
+
+                // Create another string to receive secondary HTML to be appended at the end
+                $secondary_output_str = "";
+
+                // Add the Heading to the HTML
+                $output_str .= "<h2 class=\"Photoshop_Main_Heading\">Contains Photoshop Information Resource Block (IRB)</h2>";
+
+                // Add Table to the HTML
+                $output_str .= "<table class=\"Photoshop_Table\" border=1>\n";
+
+                // Cycle through each of the Photoshop IRB records, creating HTML for each
+                foreach( $IRB_array as $IRB_Resource )
+                {
+                        // Check if the entry is a known Photoshop IRB resource
+
+                        // Get the Name of the Resource
+                        if ( array_key_exists( $IRB_Resource['ResID'], $GLOBALS[ "Photoshop_ID_Names" ] ) )
+                        {
+                                $Resource_Name = $GLOBALS['Photoshop_ID_Names'][ $IRB_Resource['ResID'] ];
+                        }
+                        else
+                        {
+                                // Change: Added check for $GLOBALS['HIDE_UNKNOWN_TAGS'] to allow hiding of unknown resources as of 1.11
+                                if ( $GLOBALS['HIDE_UNKNOWN_TAGS'] == TRUE )
+                                {
+                                        continue;
+                                }
+                                else
+                                {
+                                        // Unknown Resource - Make appropriate name
+                                        $Resource_Name = "Unknown Resource (". $IRB_Resource['ResID'] .")";
+                                }
+                        }
+
+                        // Add HTML for the resource as appropriate
+                        switch ( $IRB_Resource['ResID'] )
+                        {
+
+                                case 0x0404 : // IPTC-NAA IIM Record
+                                        $secondary_output_str .= Interpret_IPTC_to_HTML( get_IPTC( $IRB_Resource['ResData'] ) );
+                                        break;
+
+                                case 0x040B : // URL
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><a href=\"" . $IRB_Resource['ResData'] . "\">" . htmlentities( $IRB_Resource['ResData'] ) ."</a></td></tr>\n";
+                                        break;
+
+                                case 0x040A : // Copyright Marked
+                                        if ( hexdec( bin2hex( $IRB_Resource['ResData'] ) ) == 1 )
+                                        {
+                                                $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Copyrighted Material</pre></td></tr>\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Image is Not Copyrighted Material</pre></td></tr>\n";
+                                        }
+                                        break;
+
+                                case 0x040D : // Global Lighting Angle
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global lighting angle for effects layer = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . " degrees</pre></td></tr>\n";
+                                        break;
+
+                                case 0x0419 : // Global Altitude
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Global Altitude = " . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x0421 : // Version Info
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) ) . "\n";
+                                        $output_str .= "Has Real Merged Data = " . ord( $IRB_Resource['ResData']{4} ) . "\n";
+                                        $writer_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 5, 4 ) ) ) * 2;
+
+                                        $output_str .= "Writer Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 9, $writer_size ), TRUE ) . "\n";
+                                        $reader_size = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 9 + $writer_size , 4 ) ) ) * 2;
+                                        $output_str .= "Reader Name = " . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 13 + $writer_size, $reader_size ), TRUE ) . "\n";
+                                        $output_str .= "File Version = " . hexdec( bin2hex( substr( $IRB_Resource['ResData'], 13 + $writer_size + $reader_size, 4 ) ) ) . "\n";
+                                        $output_str .=  "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x0411 : // ICC Untagged
+                                        if ( $IRB_Resource['ResData'] == "\x01" )
+                                        {
+                                                $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Intentionally untagged - any assumed ICC profile handling disabled</pre></td></tr>\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>Unknown value (0x" .bin2hex( $IRB_Resource['ResData'] ). ")</pre></td></tr>\n";
+                                        }
+                                        break;
+
+                                case 0x041A : // Slices
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
+
+                                        // Unpack the first 24 bytes
+                                        $Slices_Info = unpack("NVersion/NBound_top/NBound_left/NBound_bottom/NBound_right/NStringlen", $IRB_Resource['ResData'] );
+                                        $output_str .= "Version = " . $Slices_Info['Version'] . "<br>\n";
+                                        $output_str .= "Bounding Rectangle =  Top:" . $Slices_Info['Bound_top'] . ", Left:" . $Slices_Info['Bound_left'] . ", Bottom:" . $Slices_Info['Bound_bottom'] . ", Right:" . $Slices_Info['Bound_right'] . " (Pixels)<br>\n";
+                                        $Slicepos = 24;
+
+                                        // Extract a Unicode String
+                                        $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], 24, $Slices_Info['Stringlen']*2), TRUE ) . "'<br>\n";
+                                        $Slicepos += $Slices_Info['Stringlen'] * 2;
+
+                                        // Unpack the number of Slices
+                                        $Num_Slices = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
+                                        $output_str .= "Number of Slices = " . $Num_Slices . "\n";
+                                        $Slicepos += 4;
+
+                                        // Cycle through the slices
+                                        for( $i = 1; $i <= $Num_Slices; $i++ )
+                                        {
+                                                $output_str .= "<br><br>Slice $i:<br>\n";
+
+                                                // Unpack the first 16 bytes of the slice
+                                                $SliceA = unpack("NID/NGroupID/NOrigin/NStringlen", substr($IRB_Resource['ResData'], $Slicepos ) );
+                                                $Slicepos += 16;
+                                                $output_str .= "ID = " . $SliceA['ID'] . "<br>\n";
+                                                $output_str .= "Group ID = " . $SliceA['GroupID'] . "<br>\n";
+                                                $output_str .= "Origin = " . $SliceA['Origin'] . "<br>\n";
+
+                                                // Extract a Unicode String
+                                                $output_str .= "Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceA['Stringlen']*2), TRUE ) . "'<br>\n";
+                                                $Slicepos += $SliceA['Stringlen'] * 2;
+
+                                                // Unpack the next 24 bytes of the slice
+                                                $SliceB = unpack("NType/NLeftPos/NTopPos/NRightPos/NBottomPos/NURLlen", substr($IRB_Resource['ResData'], $Slicepos )  );
+                                                $Slicepos += 24;
+                                                $output_str .= "Type = " . $SliceB['Type'] . "<br>\n";
+                                                $output_str .= "Position =  Top:" . $SliceB['TopPos'] . ", Left:" . $SliceB['LeftPos'] . ", Bottom:" . $SliceB['BottomPos'] . ", Right:" . $SliceB['RightPos'] . " (Pixels)<br>\n";
+
+                                                // Extract a Unicode String
+                                                $output_str .= "URL = <a href='" . substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2) . "'>" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $SliceB['URLlen']*2), TRUE ) . "</a><br>\n";
+                                                $Slicepos += $SliceB['URLlen'] * 2;
+
+                                                // Unpack the length of a Unicode String
+                                                $Targetlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
+                                                $Slicepos += 4;
+                                                // Extract a Unicode String
+                                                $output_str .= "Target = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Targetlen*2), TRUE ) . "'<br>\n";
+                                                $Slicepos += $Targetlen * 2;
+
+                                                // Unpack the length of a Unicode String
+                                                $Messagelen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
+                                                $Slicepos += 4;
+                                                // Extract a Unicode String
+                                                $output_str .= "Message = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $Messagelen*2), TRUE ) . "'<br>\n";
+                                                $Slicepos += $Messagelen * 2;
+
+                                                // Unpack the length of a Unicode String
+                                                $AltTaglen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
+                                                $Slicepos += 4;
+                                                // Extract a Unicode String
+                                                $output_str .= "Alt Tag = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $AltTaglen*2), TRUE ) . "'<br>\n";
+                                                $Slicepos += $AltTaglen * 2;
+
+                                                // Unpack the HTML flag
+                                                if ( ord( $IRB_Resource['ResData']{ $Slicepos } ) === 0x01 )
+                                                {
+                                                        $output_str .= "Cell Text is HTML<br>\n";
+                                                }
+                                                else
+                                                {
+                                                        $output_str .= "Cell Text is NOT HTML<br>\n";
+                                                }
+                                                $Slicepos++;
+
+                                                // Unpack the length of a Unicode String
+                                                $CellTextlen = hexdec( bin2hex( substr( $IRB_Resource['ResData'], $Slicepos, 4 ) ) );
+                                                $Slicepos += 4;
+                                                // Extract a Unicode String
+                                                $output_str .= "Cell Text = '" . HTML_UTF16_Escape( substr( $IRB_Resource['ResData'], $Slicepos, $CellTextlen*2), TRUE ) . "'<br>\n";
+                                                $Slicepos += $CellTextlen * 2;
+
+
+                                                // Unpack the last 12 bytes of the slice
+                                                $SliceC = unpack("NAlignH/NAlignV/CAlpha/CRed/CGreen/CBlue", substr($IRB_Resource['ResData'], $Slicepos )  );
+                                                $Slicepos += 12;
+                                                $output_str .= "Alignment =  Horizontal:" . $SliceC['AlignH'] . ", Vertical:" . $SliceC['AlignV'] . "<br>\n";
+                                                $output_str .= "Alpha Colour = " . $SliceC['Alpha'] . "<br>\n";
+                                                $output_str .= "Red = " . $SliceC['Red'] . "<br>\n";
+                                                $output_str .= "Green = " . $SliceC['Green'] . "<br>\n";
+                                                $output_str .= "Blue = " . $SliceC['Blue'] . "\n";
+                                        }
+
+                                        $output_str .= "</td></tr>\n";
+
+                                        break;
+
+
+                                case 0x0408 : // Grid and Guides information
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
+
+                                        // Unpack the Grids info
+                                        $Grid_Info = unpack("NVersion/NGridCycleH/NGridCycleV/NGuideCount", $IRB_Resource['ResData'] );
+                                        $output_str .= "Version = " . $Grid_Info['Version'] . "<br>\n";
+                                        $output_str .= "Grid Cycle = " . $Grid_Info['GridCycleH']/32 . " Pixel(s)  x  " . $Grid_Info['GridCycleV']/32 . " Pixel(s)<br>\n";
+                                        $output_str .= "Number of Guides = " . $Grid_Info['GuideCount'] . "\n";
+
+                                        // Cycle through the Guides
+                                        for( $i = 0; $i < $Grid_Info['GuideCount']; $i++ )
+                                        {
+                                                // Unpack the info for this guide
+                                                $Guide_Info = unpack("NLocation/CDirection", substr($IRB_Resource['ResData'],16+$i*5,5) );
+                                                $output_str .= "<br>Guide $i : Location = " . $Guide_Info['Location']/32 . " Pixel(s) from edge";
+                                                if ( $Guide_Info['Direction'] === 0 )
+                                                {
+                                                        $output_str .= ", Vertical\n";
+                                                }
+                                                else
+                                                {
+                                                        $output_str .= ", Horizontal\n";
+                                                }
+                                        }
+                                        break;
+                                        $output_str .= "</td></tr>\n";
+
+                                case 0x0406 : // JPEG Quality
+                                        $Qual_Info = unpack("nQuality/nFormat/nScans/Cconst", $IRB_Resource['ResData'] );
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">";
+                                        switch ( $Qual_Info['Quality'] )
+                                        {
+                                                case 0xFFFD:
+                                                        $output_str .= "Quality 1 (Low)<br>\n";
+                                                        break;
+                                                case 0xFFFE:
+                                                        $output_str .= "Quality 2 (Low)<br>\n";
+                                                        break;
+                                                case 0xFFFF:
+                                                        $output_str .= "Quality 3 (Low)<br>\n";
+                                                        break;
+                                                case 0x0000:
+                                                        $output_str .= "Quality 4 (Low)<br>\n";
+                                                        break;
+                                                case 0x0001:
+                                                        $output_str .= "Quality 5 (Medium)<br>\n";
+                                                        break;
+                                                case 0x0002:
+                                                        $output_str .= "Quality 6 (Medium)<br>\n";
+                                                        break;
+                                                case 0x0003:
+                                                        $output_str .= "Quality 7 (Medium)<br>\n";
+                                                        break;
+                                                case 0x0004:
+                                                        $output_str .= "Quality 8 (High)<br>\n";
+                                                        break;
+                                                case 0x0005:
+                                                        $output_str .= "Quality 9 (High)<br>\n";
+                                                        break;
+                                                case 0x0006:
+                                                        $output_str .= "Quality 10 (Maximum)<br>\n";
+                                                        break;
+                                                case 0x0007:
+                                                        $output_str .= "Quality 11 (Maximum)<br>\n";
+                                                        break;
+                                                case 0x0008:
+                                                        $output_str .= "Quality 12 (Maximum)<br>\n";
+                                                        break;
+                                                default:
+                                                        $output_str .= "Unknown Quality (" . $Qual_Info['Quality'] . ")<br>\n";
+                                                        break;
+                                        }
+
+                                        switch ( $Qual_Info['Format'] )
+                                        {
+                                                case 0x0000:
+                                                        $output_str .= "Standard Format\n";
+                                                        break;
+                                                case 0x0001:
+                                                        $output_str .= "Optimised Format\n";
+                                                        break;
+                                                case 0x0101:
+                                                        $output_str .= "Progressive Format<br>\n";
+                                                        break;
+                                                default:
+                                                        $output_str .= "Unknown Format (" . $Qual_Info['Format'] .")\n";
+                                                        break;
+                                        }
+                                        if ( $Qual_Info['Format'] == 0x0101 )
+                                        {
+                                                switch ( $Qual_Info['Scans'] )
+                                                {
+                                                        case 0x0001:
+                                                                $output_str .= "3 Scans\n";
+                                                                break;
+                                                        case 0x0002:
+                                                                $output_str .= "4 Scans\n";
+                                                                break;
+                                                        case 0x0003:
+                                                                $output_str .= "5 Scans\n";
+                                                                break;
+                                                        default:
+                                                                $output_str .= "Unknown number of scans (" . $Qual_Info['Scans'] .")\n";
+                                                                break;
+                                                }
+                                        }
+                                        $output_str .= "</td></tr>\n";
+                                        break;
+
+                                case 0x0409 : // Thumbnail Resource
+                                case 0x040C : // Thumbnail Resource
+                                        $thumb_data = unpack("NFormat/NWidth/NHeight/NWidthBytes/NSize/NCompressedSize/nBitsPixel/nPlanes", $IRB_Resource['ResData'] );
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Format = " . (( $thumb_data['Format'] == 1 ) ? "JPEG RGB\n" :  "Raw RGB\n");
+                                        $output_str .= "Width = " . $thumb_data['Width'] . "\n";
+                                        $output_str .= "Height = " . $thumb_data['Height'] . "\n";
+                                        $output_str .= "Padded Row Bytes = " . $thumb_data['WidthBytes'] . " bytes\n";
+                                        $output_str .= "Total Size = " . $thumb_data['Size'] . " bytes\n";
+                                        $output_str .= "Compressed Size = " . $thumb_data['CompressedSize'] . " bytes\n";
+                                        $output_str .= "Bits per Pixel = " . $thumb_data['BitsPixel'] . " bits\n";
+                                        $output_str .= "Number of planes = " . $thumb_data['Planes'] . " bytes\n";
+
+                                        // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                                        // Build the path of the thumbnail script and its filename parameter to put in a url
+                                        $link_str = get_relative_path( dirname(__FILE__) . "/get_ps_thumb.php" , getcwd ( ) );
+                                        $link_str .= "?filename=";
+                                        $link_str .= get_relative_path( $filename, dirname(__FILE__) );
+
+                                        // Add thumbnail link to html
+                                        $output_str .= "Thumbnail Data:</pre><a class=\"Photoshop_Thumbnail_Link\" href=\"$link_str\"><img class=\"Photoshop_Thumbnail_Link\" src=\"$link_str\"></a>\n";
+
+                                        $output_str .=  "</td></tr>\n";
+                                        break;
+
+                                case 0x0414 : // Document Specific ID's
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>" . hexdec( bin2hex( $IRB_Resource['ResData'] ) ) . "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x041E : // URL List
+                                        $URL_count = hexdec( bin2hex( substr( $IRB_Resource['ResData'], 0, 4 ) ) );
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">\n";
+                                        $output_str .= "$URL_count URL's in list<br>\n";
+                                        $urlstr = substr( $IRB_Resource['ResData'], 4 );
+                                        // TODO: Check if URL List in Photoshop IRB works
+                                        for( $i = 0; $i < $URL_count; $i++ )
+                                        {
+                                                $url_data = unpack( "NLong/NID/NURLSize", $urlstr );
+                                                $output_str .= "URL $i info: long = " . $url_data['Long'] .", ";
+                                                $output_str .= "ID = " . $url_data['ID'] . ", ";
+                                                $urlstr = substr( $urlstr, 12 );
+                                                $url = substr( $urlstr, 0, $url_data['URLSize'] );
+                                                $output_str .= "URL = <a href=\"" . xml_UTF16_clean( $url, TRUE ) . "\">" . HTML_UTF16_Escape( $url, TRUE ) . "</a><br>\n";
+                                        }
+                                        $output_str .= "</td></tr>\n";
+                                        break;
+                                case 0x03F4 : // Grayscale and multichannel halftoning information.
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= Interpret_Halftone( $IRB_Resource['ResData'] );
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+                                case 0x03F5 : // Color halftoning information
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Cyan Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 0, 18 ) ) . "\n\n";
+                                        $output_str .= "Magenta Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 18, 18 ) ) . "\n\n";
+                                        $output_str .= "Yellow Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 36, 18 ) ) . "\n";
+                                        $output_str .= "Black Halftoning Info:\n" . Interpret_Halftone( substr( $IRB_Resource['ResData'], 54, 18 ) ) . "\n";
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x03F7 : // Grayscale and multichannel transfer function.
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) ;
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x03F8 : // Color transfer functions
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Red Transfer Function:   \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 0, 28 ) ) . "\n\n";
+                                        $output_str .= "Green Transfer Function: \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 28, 28 ) ) . "\n\n";
+                                        $output_str .= "Blue Transfer Function:  \n" . Interpret_Transfer_Function( substr( $IRB_Resource['ResData'], 56, 28 ) ) . "\n";
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x03F3 : // Print Flags
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        if ( $IRB_Resource['ResData']{0} == "\x01" )
+                                        {
+                                                $output_str .= "Labels Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Labels Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{1} == "\x01" )
+                                        {
+                                                $output_str .= "Crop Marks Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Crop Marks Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{2} == "\x01" )
+                                        {
+                                                $output_str .= "Color Bars Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Color Bars Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{3} == "\x01" )
+                                        {
+                                                $output_str .= "Registration Marks Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Registration Marks Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{4} == "\x01" )
+                                        {
+                                                $output_str .= "Negative Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Negative Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{5} == "\x01" )
+                                        {
+                                                $output_str .= "Flip Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Flip Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{6} == "\x01" )
+                                        {
+                                                $output_str .= "Interpolate Selected\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Interpolate Not Selected\n";
+                                        }
+                                        if ( $IRB_Resource['ResData']{7} == "\x01" )
+                                        {
+                                                $output_str .= "Caption Selected";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Caption Not Selected";
+                                        }
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x2710 : // Print Flags Information
+                                        $PrintFlags = unpack( "nVersion/CCentCrop/Cjunk/NBleedWidth/nBleedWidthScale", $IRB_Resource['ResData'] );
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Version = " . $PrintFlags['Version'] . "\n";
+                                        $output_str .= "Centre Crop Marks = " . $PrintFlags['CentCrop'] . "\n";
+                                        $output_str .= "Bleed Width = " . $PrintFlags['BleedWidth'] . "\n";
+                                        $output_str .= "Bleed Width Scale = " . $PrintFlags['BleedWidthScale'];
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                case 0x03ED : // Resolution Info
+                                        $ResInfo = unpack( "nhRes_int/nhResdec/nhResUnit/nwidthUnit/nvRes_int/nvResdec/nvResUnit/nheightUnit", $IRB_Resource['ResData'] );
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\"><pre>\n";
+                                        $output_str .= "Horizontal Resolution = " . ($ResInfo['hRes_int'] + $ResInfo['hResdec']/65536) . " pixels per Inch\n";
+                                        $output_str .= "Vertical Resolution = " . ($ResInfo['vRes_int'] + $ResInfo['vResdec']/65536) . " pixels per Inch\n";
+                                        if ( $ResInfo['hResUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Inch\n";
+                                        }
+                                        elseif ( $ResInfo['hResUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Pixels per Centimetre\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Horizontal Resolution = Unknown Value (". $ResInfo['hResUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['vResUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Pixels per Inch\n";
+                                        }
+                                        elseif ( $ResInfo['vResUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Pixels per Centimetre\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Vertical Resolution = Unknown Value (". $ResInfo['vResUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['widthUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Inches\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Centimetres\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 3 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Points\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 4 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Picas\n";
+                                        }
+                                        elseif ( $ResInfo['widthUnit'] == 5 )
+                                        {
+                                                $output_str .= "Display units for Image Width = Columns\n";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Image Width = Unknown Value (". $ResInfo['widthUnit'] .")\n";
+                                        }
+
+                                        if ( $ResInfo['heightUnit'] == 1 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Inches";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 2 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Centimetres";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 3 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Points";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 4 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Picas";
+                                        }
+                                        elseif ( $ResInfo['heightUnit'] == 5 )
+                                        {
+                                                $output_str .= "Display units for Image Height = Columns";
+                                        }
+                                        else
+                                        {
+                                                $output_str .= "Display units for Image Height = Unknown Value (". $ResInfo['heightUnit'] .")";
+                                        }
+                                        $output_str .= "</pre></td></tr>\n";
+                                        break;
+
+                                default : // All other records
+                                        $output_str .= "<tr class=\"Photoshop_Table_Row\"><td class=\"Photoshop_Caption_Cell\">$Resource_Name</td><td class=\"Photoshop_Value_Cell\">RESOURCE DECODING NOT IMPLEMENTED YET<BR>" . strlen( $IRB_Resource['ResData'] ) . " bytes</td></tr>\n";
+
+                        }
+
+                }
+
+                // Add the table end to the HTML
+                $output_str .= "</table>\n";
+
+                // Add any secondary output to the HTML
+                $output_str .= $secondary_output_str;
+
+        }
+
+        // Return the HTML
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_IRB_to_HTML
+******************************************************************************/
+
+
+
+
+
+
+/******************************************************************************
+*
+*         INTERNAL FUNCTIONS
+*
+******************************************************************************/
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     unpack_Photoshop_IRB_Data
+*
+* Description:  Extracts Photoshop Information Resource Block (IRB) information
+*               from a binary string containing the IRB, as read from a file
+*
+* Parameters:   IRB_Data - The binary string containing the IRB
+*
+* Returns:      IRBdata - The array of Photoshop IRB records
+*
+******************************************************************************/
+
+function unpack_Photoshop_IRB_Data( $IRB_Data )
+{
+        $pos = 0;
+
+        // Cycle through the IRB and extract its records - Records are started with 8BIM, so cycle until no more instances of 8BIM can be found
+        while ( ( $pos < strlen( $IRB_Data ) ) && ( ($pos = strpos( $IRB_Data, "8BIM", $pos) ) !== FALSE ) )
+        {
+                // Skip the position over the 8BIM characters
+                $pos += 4;
+
+                // Next two characters are the record ID - denoting what type of record it is.
+                $ID = ord( $IRB_Data{ $pos } ) * 256 + ord( $IRB_Data{ $pos +1 } );
+
+                // Skip the positionover the two record ID characters
+                $pos += 2;
+
+                // Next comes a Record Name - usually not used, but it should be a null terminated string, padded with 0x00 to be an even length
+                $namestartpos = $pos;
+
+                // Change: Fixed processing of embedded resource names, as of revision 1.10
+
+                // NOTE: Photoshop does not process resource names according to the standard :
+                // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
+                //
+                // The resource name is actually formatted as follows:
+                // One byte name length, followed by the null terminated ascii name string.
+                // The field is then padded with a Null character if required, to ensure that the
+                // total length of the name length and name is even.
+
+                // Name - process it
+                // Get the length
+                $namelen = ord ( $IRB_Data{ $namestartpos } );
+
+                // Total length of name and length info must be even, hence name length must be odd
+                // Check if the name length is even,
+                if ( $namelen % 2 == 0 )
+                {
+                        // add one to length to make it odd
+                        $namelen ++;
+                }
+                // Extract the name
+                $resembeddedname = trim( substr ( $IRB_Data, $namestartpos+1,  $namelen) );
+                $pos += $namelen + 1;
+
+
+                // Next is a four byte size field indicating the size in bytes of the record's data  - MSB first
+                $datasize =     ord( $IRB_Data{ $pos } ) * 16777216 + ord( $IRB_Data{ $pos + 1 } ) * 65536 +
+                                ord( $IRB_Data{ $pos + 2 } ) * 256 + ord( $IRB_Data{ $pos + 3 } );
+                $pos += 4;
+
+                // The record is stored padded with 0x00 characters to make the size even, so we need to calculate the stored size
+                $storedsize =  $datasize + ($datasize % 2);
+
+                $resdata = substr ( $IRB_Data, $pos, $datasize );
+
+                // Get the description for this resource
+                // Check if this is a Path information Resource, since they have a range of ID's
+                if ( ( $ID >= 0x07D0 ) && ( $ID <= 0x0BB6 ) )
+                {
+                        $ResDesc = "ID Info : Path Information (saved paths).";
+                }
+                else
+                {
+                        if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Descriptions" ] ) )
+                        {
+                                $ResDesc = $GLOBALS[ "Photoshop_ID_Descriptions" ][ $ID ];
+                        }
+                        else
+                        {
+                                $ResDesc = "";
+                        }
+                }
+
+                // Get the Name of the Resource
+                if ( array_key_exists( $ID, $GLOBALS[ "Photoshop_ID_Names" ] ) )
+                {
+                        $ResName = $GLOBALS['Photoshop_ID_Names'][ $ID ];
+                }
+                else
+                {
+                        $ResName = "";
+                }
+
+
+                // Store the Resource in the array to be returned
+
+                $IRB_Array[] = array(     "ResID" => $ID,
+                                        "ResName" => $ResName,
+                                        "ResDesc" => $ResDesc,
+                                        "ResEmbeddedName" => $resembeddedname,
+                                        "ResData" => $resdata );
+
+                // Jump over the data to the next record
+                $pos += $storedsize;
+        }
+
+        // Return the array created
+        return $IRB_Array;
+}
+
+/******************************************************************************
+* End of Function:     unpack_Photoshop_IRB_Data
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     pack_Photoshop_IRB_Data
+*
+* Description:  Packs a Photoshop Information Resource Block (IRB) array into it's
+*               binary form, which can be written to a file
+*
+* Parameters:   IRB_data - an Photoshop IRB array to be converted. Should be in
+*                          the same format as received from get_Photoshop_IRB
+*
+* Returns:      packed_IRB_data - the binary string of packed IRB data
+*
+******************************************************************************/
+
+function pack_Photoshop_IRB_Data( $IRB_data )
+{
+        $packed_IRB_data = "";
+
+        // Cycle through each resource in the IRB,
+        foreach ($IRB_data as $resource)
+        {
+
+                // Change: Fix to avoid creating blank resources, as of revision 1.10
+
+                // Check if there is actually any data for this resource
+                if( strlen( $resource['ResData'] ) == 0 )
+                {
+                        // No data for resource - skip it
+                        continue;
+                }
+
+                // Append the 8BIM tag, and resource ID to the packed output data
+                $packed_IRB_data .= pack("a4n", "8BIM", $resource['ResID'] );
+
+
+                // Change: Fixed processing of embedded resource names, as of revision 1.10
+
+                // NOTE: Photoshop does not process resource names according to the standard :
+                // "Adobe Photoshop 6.0 File Formats Specification, Version 6.0, Release 2, November 2000"
+                //
+                // The resource name is actually formatted as follows:
+                // One byte name length, followed by the null terminated ascii name string.
+                // The field is then padded with a Null character if required, to ensure that the
+                // total length of the name length and name is even.
+
+                // Append Name Size
+                $packed_IRB_data .= pack( "c", strlen(trim($resource['ResEmbeddedName'])));
+
+                // Append the Resource Name to the packed output data
+                $packed_IRB_data .= trim($resource['ResEmbeddedName']);
+
+                // If the resource name is even length, then with the addition of
+                // the size it becomes odd and needs to be padded to an even number
+                if ( strlen( trim($resource['ResEmbeddedName']) ) % 2 == 0 )
+                {
+                        // then it needs to be evened up by appending another null
+                        $packed_IRB_data .= "\x00";
+                }
+
+                // Append the resource data size to the packed output data
+                $packed_IRB_data .= pack("N", strlen( $resource['ResData'] ) );
+
+                // Append the resource data to the packed output data
+                $packed_IRB_data .= $resource['ResData'];
+
+                // If the resource data is odd length,
+                if ( strlen( $resource['ResData'] ) % 2 == 1 )
+                {
+                        // then it needs to be evened up by appending another null
+                        $packed_IRB_data .= "\x00";
+                }
+        }
+
+        // Return the packed data string
+        return $packed_IRB_data;
+}
+
+/******************************************************************************
+* End of Function:     pack_Photoshop_IRB_Data
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     Interpret_Transfer_Function
+*
+* Description:  Used by Interpret_IRB_to_HTML to interpret Color transfer functions
+*               for Photoshop IRB resource 0x03F8. Converts the transfer function
+*               information to a human readable version.
+*
+* Parameters:   Transfer_Function_Binary - a 28 byte Ink curves structure string
+*
+* Returns:      output_str - the text string containing the transfer function
+*                            information
+*
+******************************************************************************/
+
+function Interpret_Transfer_Function( $Transfer_Function_Binary )
+{
+        // Unpack the Transfer function information
+        $Trans_vals = unpack ( "n13Curve/nOverride",  $Transfer_Function_Binary );
+
+        $output_str = "Transfer Function Points: ";
+
+        // Cycle through each of the Transfer function array values
+        foreach ( $Trans_vals as $Key => $val )
+        {
+                // Check if the value should be negative
+                if ($val > 32768 )
+                {
+                        // Value should be negative - make it so
+                        $val = $val - 65536;
+                }
+                // Check that the Override item is not getting in this list, and
+                // that the value is not -1, which means ignored
+                if ( ( $Key != "Override" ) && ( $val != -1 ) )
+                {
+                        // This is a valid transfer function point, output it
+                        $output_str .= $val/10 . "%, ";
+                }
+        }
+
+        // Output the override info
+        if ( $Trans_vals['Override'] == 0 )
+        {
+                $output_str .= "\nOverride: Let printer supply curve";
+        }
+        else
+        {
+                $output_str .= "\nOverride: Override printer’s default transfer curve";
+        }
+
+        // Return the result
+        return $output_str;
+}
+
+/******************************************************************************
+* End of Function:     Interpret_Transfer_Function
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+*
+* Internal Function:     Interpret_Halftone
+*
+* Description:  Used by Interpret_IRB_to_HTML to interpret Color halftoning information
+*               for Photoshop IRB resource 0x03F5. Converts the halftoning info
+*               to a human readable version.
+*
+* Parameters:   Transfer_Function_Binary - a 18 byte Halftone screen parameter
+&                                          structure string
+*
+* Returns:      output_str - the text string containing the transfer function
+*                            information
+*
+******************************************************************************/
+
+function Interpret_Halftone( $Halftone_Binary )
+{
+        // Create a string to receive the output
+        $output_str = "";
+
+        // Unpack the binary data into an array
+        $HalftoneInfo = unpack( "nFreqVal_int/nFreqVal_dec/nFreqScale/nAngle_int/nAngle_dec/nShapeCode/NMisc/CAccurate/CDefault", $Halftone_Binary );
+
+        // Interpret Ink Screen Frequency
+        $output_str .= "Ink Screen Frequency = " . ($HalftoneInfo['FreqVal_int'] + $HalftoneInfo['FreqVal_dec']/65536) . " lines per Inch\n";
+        if ( $HalftoneInfo['FreqScale'] == 1 )
+        {
+                $output_str .= "Display units for Ink Screen Frequency = Inches\n";
+        }
+        else
+        {
+                $output_str .= "Display units for Ink Screen Frequency = Centimetres\n";
+        }
+
+        // Interpret Angle for screen
+        $output_str .= "Angle for screen = " . ($HalftoneInfo['Angle_int'] + $HalftoneInfo['Angle_dec']/65536) . " degrees\n";
+
+        // Interpret Shape of Halftone Dots
+        if ($HalftoneInfo['ShapeCode'] > 32768 )
+        {
+                $HalftoneInfo['ShapeCode'] = $HalftoneInfo['ShapeCode'] - 65536;
+        }
+        if ( $HalftoneInfo['ShapeCode'] == 0 )
+        {
+                $output_str .= "Shape of Halftone Dots = Round\n";
+        }
+        elseif ( $HalftoneInfo['ShapeCode'] == 1 )
+        {
+                $output_str .= "Shape of Halftone Dots = Ellipse\n";
+        }
+        elseif ( $HalftoneInfo['ShapeCode'] == 2 )
+        {
+                $output_str .= "Shape of Halftone Dots = Line\n";
+        }
+        elseif ( $HalftoneInfo['ShapeCode'] == 3 )
+        {
+                $output_str .= "Shape of Halftone Dots = Square\n";
+        }
+        elseif ( $HalftoneInfo['ShapeCode'] == 4 )
+        {
+                $output_str .= "Shape of Halftone Dots = Cross\n";
+        }
+        elseif ( $HalftoneInfo['ShapeCode'] == 6 )
+        {
+                $output_str .= "Shape of Halftone Dots = Diamond\n";
+        }
+        else
+        {
+                $output_str .= "Shape of Halftone Dots = Unknown shape (" . $HalftoneInfo['ShapeCode'] . ")\n";
+        }
+
+        // Interpret Accurate Screens
+        if ( $HalftoneInfo['Accurate'] == 1 )
+        {
+                $output_str .= "Use Accurate Screens Selected\n";
+        }
+        else
+        {
+                $output_str .= "Use Other (not Accurate) Screens Selected\n";
+        }
+
+        // Interpret Printer Default Screens
+        if ( $HalftoneInfo['Default'] == 1 )
+        {
+                $output_str .= "Use printer’s default screens\n";
+        }
+        else
+        {
+                $output_str .= "Use Other (not Printer Default) Screens Selected\n";
+        }
+
+        // Return Text
+        return $output_str;
+
+}
+
+/******************************************************************************
+* End of Global Variable:     Interpret_Halftone
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Photoshop_ID_Names
+*
+* Contents:     The Names of the Photoshop IRB resources, indexed by their
+*               resource number
+*
+******************************************************************************/
+
+$GLOBALS[ "Photoshop_ID_Names" ] = array(
+0x03E8 => "Number of channels, rows, columns, depth, and mode. (Obsolete)",
+0x03E9 => "Macintosh print manager info ",
+0x03EB => "Indexed color table (Obsolete)",
+0x03ED => "Resolution Info",
+0x03EE => "Alpha Channel Names",
+0x03EF => "Display Info",
+0x03F0 => "Caption String",
+0x03F1 => "Border information",
+0x03F2 => "Background color",
+0x03F3 => "Print flags",
+0x03F4 => "Grayscale and multichannel halftoning information",
+0x03F5 => "Color halftoning information",
+0x03F6 => "Duotone halftoning information",
+0x03F7 => "Grayscale and multichannel transfer function",
+0x03F8 => "Color transfer functions",
+0x03F9 => "Duotone transfer functions",
+0x03FA => "Duotone image information",
+0x03FB => "Black and white values",
+0x03FC => "Obsolete Resource.",
+0x03FD => "EPS options",
+0x03FE => "Quick Mask information",
+0x03FF => "Obsolete Resource",
+0x0400 => "Layer state information",
+0x0401 => "Working path (not saved)",
+0x0402 => "Layers group information",
+0x0403 => "Obsolete Resource",
+0x0404 => "IPTC-NAA record",
+0x0405 => "Raw Format Image mode",
+0x0406 => "JPEG quality",
+0x0408 => "Grid and guides information",
+0x0409 => "Thumbnail resource",
+0x040A => "Copyright flag",
+0x040B => "URL",
+0x040C => "Thumbnail resource",
+0x040D => "Global Angle",
+0x040E => "Color samplers resource",
+0x040F => "ICC Profile",
+0x0410 => "Watermark",
+0x0411 => "ICC Untagged",
+0x0412 => "Effects visible",
+0x0413 => "Spot Halftone",
+0x0414 => "Document Specific IDs",
+0x0415 => "Unicode Alpha Names",
+0x0416 => "Indexed Color Table Count",
+0x0417 => "Tansparent Index. Index of transparent color, if any.",
+0x0419 => "Global Altitude",
+0x041A => "Slices",
+0x041B => "Workflow URL",
+0x041C => "Jump To XPEP",
+0x041D => "Alpha Identifiers",
+0x041E => "URL List",
+0x0421 => "Version Info",
+0x0BB7 => "Name of clipping path.",
+0x2710 => "Print flags information"
+);
+
+/******************************************************************************
+* End of Global Variable:     Photoshop_ID_Names
+******************************************************************************/
+
+
+
+
+
+/******************************************************************************
+* Global Variable:      Photoshop_ID_Descriptions
+*
+* Contents:     The Descriptions of the Photoshop IRB resources, indexed by their
+*               resource number
+*
+******************************************************************************/
+
+$GLOBALS[ "Photoshop_ID_Descriptions" ] = array(
+0x03E8 => "Obsolete—Photoshop 2.0 only. number of channels, rows, columns, depth, and mode.",
+0x03E9 => "Optional. Macintosh print manager print info record.",
+0x03EB => "Obsolete—Photoshop 2.0 only. Contains the indexed color table.",
+0x03ED => "ResolutionInfo structure. See Appendix A in Photoshop SDK Guide.pdf",
+0x03EE => "Names of the alpha channels as a series of Pascal strings.",
+0x03EF => "DisplayInfo structure. See Appendix A in Photoshop SDK Guide.pdf",
+0x03F0 => "Optional. The caption as a Pascal string.",
+0x03F1 => "Border information. border width, border units",
+0x03F2 => "Background color.",
+0x03F3 => "Print flags. labels, crop marks, color bars, registration marks, negative, flip, interpolate, caption.",
+0x03F4 => "Grayscale and multichannel halftoning information.",
+0x03F5 => "Color halftoning information.",
+0x03F6 => "Duotone halftoning information.",
+0x03F7 => "Grayscale and multichannel transfer function.",
+0x03F8 => "Color transfer functions.",
+0x03F9 => "Duotone transfer functions.",
+0x03FA => "Duotone image information.",
+0x03FB => "Effective black and white values for the dot range.",
+0x03FC => "Obsolete Resource.",
+0x03FD => "EPS options.",
+0x03FE => "Quick Mask information. Quick Mask channel ID, Mask initially empty.",
+0x03FF => "Obsolete Resource.",
+0x0400 => "Layer state information. Index of target layer.",
+0x0401 => "Working path (not saved).",
+0x0402 => "Layers group information. Group ID for the dragging groups. Layers in a group have the same group ID.",
+0x0403 => "Obsolete Resource.",
+0x0404 => "IPTC-NAA record. This contains the File Info... information. See the IIMV4.pdf document.",
+0x0405 => "Image mode for raw format files.",
+0x0406 => "JPEG quality. Private.",
+0x0408 => "Grid and guides information.",
+0x0409 => "Thumbnail resource.",
+0x040A => "Copyright flag. Boolean indicating whether image is copyrighted. Can be set via Property suite or by user in File Info...",
+0x040B => "URL. Handle of a text string with uniform resource locator. Can be set via Property suite or by user in File Info...",
+0x040C => "Thumbnail resource.",
+0x040D => "Global Angle. Global lighting angle for effects layer.",
+0x040E => "Color samplers resource.",
+0x040F => "ICC Profile. The raw bytes of an ICC format profile, see the ICC34.pdf and ICC34.h files from the Internation Color Consortium.",
+0x0410 => "Watermark.",
+0x0411 => "ICC Untagged. Disables any assumed profile handling when opening the file. 1 = intentionally untagged.",
+0x0412 => "Effects visible. Show/hide all the effects layer.",
+0x0413 => "Spot Halftone. Version, length, variable length data.",
+0x0414 => "Document specific IDs for layer identification",
+0x0415 => "Unicode Alpha Names. Length and the string",
+0x0416 => "Indexed Color Table Count. Number of colors in table that are actually defined",
+0x0417 => "Transparent Index. Index of transparent color, if any.",
+0x0419 => "Global Altitude.",
+0x041A => "Slices.",
+0x041B => "Workflow URL. Length, string.",
+0x041C => "Jump To XPEP. Major version, Minor version, Count. Table which can include: Dirty flag, Mod date.",
+0x041D => "Alpha Identifiers.",
+0x041E => "URL List. Count of URLs, IDs, and strings",
+0x0421 => "Version Info. Version, HasRealMergedData, string of writer name, string of reader name, file version.",
+0x0BB7 => "Name of clipping path.",
+0x2710 => "Print flags information. Version, Center crop marks, Bleed width value, Bleed width scale."
+);
+
+/******************************************************************************
+* End of Global Variable:     Photoshop_ID_Descriptions
+******************************************************************************/
+
+
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/get_JFXX_thumb.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/get_JFXX_thumb.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/get_JFXX_thumb.php	2011/08/17 20:08:59	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/get_JFXX_thumb.php	2004/08/05 16:08:24
@@ -0,0 +1,109 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     get_JFXX_thumb.php
+*
+* Description:  This script extracts a JFXX (JPEG File Interchange Format
+*               Extension) thumbnail from within a JPEG file and allows it
+*               to be displayed
+*
+* Usage:        get_JFXX_thumb?filename=<filename>
+*
+* Author:       Evan Hunter
+*
+* Date:         23/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.00
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+
+        // Ensure that nothing can write to the standard io, before we get the header out
+        ob_start( );
+
+
+        include 'JPEG.php';
+        include 'JFIF.php';
+
+
+        // retrieve the filename from the URL parameters
+        
+        $filename = $GLOBALS['HTTP_GET_VARS']['filename'];
+
+        // Retrieve the JPEG header Data
+        
+        $jpeg_header_data = get_jpeg_header_data( $filename );
+
+        // Retrieve any JFXX data in the file
+
+        $JFXX_array = get_JFXX( $jpeg_header_data );
+
+        // Check if JFXX data was retrieved
+
+        if ( $JFXX_array === FALSE )
+        {
+                // No JFXX data could be retrieved - abort
+                ob_end_clean ( );
+                echo "<p>JFXX Data could not be retrieved</p>\n";
+                return;
+        }
+
+        // Check the JFXX extension code which indicates what format
+        // the thumbnail is encoded with
+        
+        if ( $JFXX_array['Extension_Code'] == 0x10 ) // JPEG Encoding
+        {
+                // JPEG Encoding - Output JPEG Data
+                ob_end_clean ( );
+                header("Content-type: image/jpeg");
+                print $JFXX_array['ThumbData'];
+                return;
+        }
+        else if ( $JFXX_array['Extension_Code'] == 0x11 ) // One Byte Per Pixel Encoding
+        {
+                // TODO: Implement decoding of One Byte Per Pixel encoded JFXX Thumbnail
+                return;
+        }
+        else if ( $JFXX_array['Extension_Code'] == 0x13 ) // Three Bytes Per Pixel Encoding
+        {
+                // TODO: Implement decoding of Three Bytes Per Pixel encoded JFXX Thumbnail
+                return;
+        }
+        else
+        {
+                // Invalid Extension Value - abort
+                return;
+        }
+
+
+
+?>
===================================================================
RCS file: ./PHP_JPEG_Metadata_Toolkit_1.11/RCS/JFIF.php,v
retrieving revision 1.1
diff -u -r1.1 ./PHP_JPEG_Metadata_Toolkit_1.11/JFIF.php
--- ./PHP_JPEG_Metadata_Toolkit_1.11/JFIF.php	2011/08/17 20:09:00	1.1
+++ ./PHP_JPEG_Metadata_Toolkit_1.11/JFIF.php	2005/01/20 09:52:26
@@ -0,0 +1,438 @@
+<?php
+
+/******************************************************************************
+*
+* Filename:     JFIF.php
+*
+* Description:  Provides functions for reading and writing information to/from
+*               JPEG File Interchange Format (JFIF) segments and
+*               JFIF Extension (JFXX) segments within a JPEG file.
+*
+* Author:       Evan Hunter
+*
+* Date:         24/7/2004
+*
+* Project:      PHP JPEG Metadata Toolkit
+*
+* Revision:     1.11
+*
+* Changes:      1.00 -> 1.11 : changed Interpret_JFXX_to_HTML to allow thumbnail links to work when
+*                              toolkit is portable across directories
+*
+* URL:          http://electronics.ozhiker.com
+*
+* Copyright:    Copyright Evan Hunter 2004
+*
+* License:      This file is part of the PHP JPEG Metadata Toolkit.
+*
+*               The PHP JPEG Metadata Toolkit is free software; you can
+*               redistribute it and/or modify it under the terms of the
+*               GNU General Public License as published by the Free Software
+*               Foundation; either version 2 of the License, or (at your
+*               option) any later version.
+*
+*               The PHP JPEG Metadata Toolkit is distributed in the hope
+*               that it will be useful, but WITHOUT ANY WARRANTY; without
+*               even the implied warranty of MERCHANTABILITY or FITNESS
+*               FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+*               for more details.
+*
+*               You should have received a copy of the GNU General Public
+*               License along with the PHP JPEG Metadata Toolkit; if not,
+*               write to the Free Software Foundation, Inc., 59 Temple
+*               Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*               If you require a different license for commercial or other
+*               purposes, please contact the author: evan@ozhiker.com
+*
+******************************************************************************/
+
+include_once 'pjmt_utils.php';          // Change: as of version 1.11 - added to allow directory portability
+
+/******************************************************************************
+*
+* Function:     get_JFIF
+*
+* Description:  Retrieves information from a JPEG File Interchange Format (JFIF)
+*               segment and returns it in an array. Uses information supplied by
+*               the get_jpeg_header_data function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      JFIF_data - an array of JFIF data
+*               FALSE - if a JFIF segment could not be found
+*
+******************************************************************************/
+
+function get_JFIF( $jpeg_header_data )
+{
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP0 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
+                {
+                        // And if it has the JFIF label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 )
+                        {
+                                // Found a JPEG File Interchange Format (JFIF) Block
+
+                                // unpack the JFIF data from the incoming string
+                                // First is the JFIF label string
+                                // Then a two byte version number
+                                // Then a byte, units identifier, ( 0 = aspect ration, 1 = dpi, 2 = dpcm)
+                                // Then a two byte int X-Axis pixel Density (resolution)
+                                // Then a two byte int Y-Axis pixel Density (resolution)
+                                // Then a byte X-Axis JFIF thumbnail size
+                                // Then a byte Y-Axis JFIF thumbnail size
+                                // Then the uncompressed RGB JFIF thumbnail data
+
+                                $JFIF_data = unpack( 'a5JFIF/C2Version/CUnits/nXDensity/nYDensity/CThumbX/CThumbY/a*ThumbData', $jpeg_header_data[$i]['SegData'] );
+
+                                return $JFIF_data;
+                        }
+                }
+        }
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_JFIF
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_JFIF
+*
+* Description:  Creates a new JFIF segment from an array of JFIF data in the
+*               same format as would be retrieved from get_JFIF, and inserts
+*               this segment into the supplied JPEG header array
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data, into which the
+*                                  new JFIF segment will be put
+*               new_JFIF_array - a JFIF information array in the same format as
+*                                from get_JFIF, to create the new segment
+*
+* Returns:      jpeg_header_data - the JPEG header data array with the new
+*                                  JFIF segment added
+*
+******************************************************************************/
+
+function put_JFIF( $jpeg_header_data, $new_JFIF_array )
+{
+        // pack the JFIF data into its proper format for a JPEG file
+        $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", $new_JFIF_array['Version1'], $new_JFIF_array['Version2'], $new_JFIF_array['Units'], $new_JFIF_array['XDensity'], $new_JFIF_array['YDensity'], $new_JFIF_array['ThumbX'], $new_JFIF_array['ThumbY'], $new_JFIF_array['ThumbData'] );
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP0 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
+                {
+                        // And if it has the JFIF label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFIF\x00", 5) == 0 )
+                        {
+                                // Found a preexisting JFIF block - Replace it with the new one and return.
+                                $jpeg_header_data[$i]['SegData'] = $packed_data;
+                                return $jpeg_header_data;
+                        }
+                }
+        }
+
+        // No preexisting JFIF block found, insert a new one at the start of the header data.
+        array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
+                                                                "SegName" => "APP0",
+                                                                "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
+                                                                "SegData" => $packed_data ) ) );
+        return $jpeg_header_data;
+}
+
+/******************************************************************************
+* End of Function:     put_JFIF
+******************************************************************************/
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_JFIF_to_HTML
+*
+* Description:  Generates html showing the JFIF information contained in
+*               a JFIF data array, as retrieved with get_JFIF
+*
+* Parameters:   JFIF_array - a JFIF data array, as from get_JFIF
+*               filename - the name of the JPEG file being processed ( used
+*                          by the script which displays the JFIF thumbnail)
+*
+*
+* Returns:      output - the HTML string
+*
+******************************************************************************/
+
+function Interpret_JFIF_to_HTML( $JFIF_array, $filename )
+{
+        $output = "";
+        if ( $JFIF_array !== FALSE )
+        {
+                $output .= "<H2 class=\"JFIF_Main_Heading\">Contains JPEG File Interchange Format (JFIF) Information</H2>\n";
+                $output .= "\n<table class=\"JFIF_Table\" border=1>\n";
+                $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF version: </td><td class=\"JFIF_Value_Cell\">". sprintf( "%d.%02d", $JFIF_array['Version1'], $JFIF_array['Version2'] ) . "</td></tr>\n";
+                if ( $JFIF_array['Units'] == 0 )
+                {
+                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Pixel Aspect Ratio: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . "</td></tr>\n";
+                }
+                elseif ( $JFIF_array['Units'] == 1 )
+                {
+                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per inch</td></tr>\n";
+                }
+                elseif ( $JFIF_array['Units'] == 2 )
+                {
+                        $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">Resolution: </td><td class=\"JFIF_Value_Cell\">" . $JFIF_array['XDensity'] ." x " . $JFIF_array['YDensity'] . " pixels per cm</td></tr>\n";
+                }
+
+                $output .= "<tr class=\"JFIF_Table_Row\"><td class=\"JFIF_Caption_Cell\">JFIF (uncompressed) thumbnail: </td><td class=\"JFIF_Value_Cell\">";
+                if ( ( $JFIF_array['ThumbX'] != 0 ) && ( $JFIF_array['ThumbY'] != 0 ) )
+                {
+                        $output .= $JFIF_array['ThumbX'] ." x " . $JFIF_array['ThumbY'] . " pixels, Thumbnail Display Not Yet Implemented</td></tr>\n";
+                        // TODO Implement JFIF Thumbnail display
+                }
+                else
+                {
+                        $output .= "None</td></tr>\n";
+                }
+
+                $output .= "</table><br>\n";
+        }
+
+        return $output;
+
+}
+
+
+/******************************************************************************
+* End of Function:     Interpret_JFIF_to_HTML
+******************************************************************************/
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*
+* Function:     get_JFXX
+*
+* Description:  Retrieves information from a JPEG File Interchange Format Extension (JFXX)
+*               segment and returns it in an array. Uses information supplied by
+*               the get_jpeg_header_data function
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data
+*
+* Returns:      JFXX_data - an array of JFXX data
+*               FALSE - if a JFXX segment could not be found
+*
+******************************************************************************/
+
+function get_JFXX( $jpeg_header_data )
+{
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP0 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
+                {
+                        // And if it has the JFIF label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 )
+                        {
+                                // Found a JPEG File Interchange Format Extension (JFXX) Block
+
+                                // unpack the JFXX data from the incoming string
+                                // First is the 5 byte JFXX label string
+                                // Then a 1 byte Extension code, indicating Thumbnail Format
+                                // Then the thumbnail data
+
+                                $JFXX_data = unpack( 'a5JFXX/CExtension_Code/a*ThumbData', $jpeg_header_data[$i]['SegData'] );
+                                return $JFXX_data;
+                        }
+                }
+        }
+        return FALSE;
+}
+
+/******************************************************************************
+* End of Function:     get_JFXX
+******************************************************************************/
+
+
+
+
+/******************************************************************************
+*
+* Function:     put_JFXX
+*
+* Description:  Creates a new JFXX segment from an array of JFXX data in the
+*               same format as would be retrieved from get_JFXX, and inserts
+*               this segment into the supplied JPEG header array
+*
+* Parameters:   jpeg_header_data - a JPEG header data array in the same format
+*                                  as from get_jpeg_header_data, into which the
+*                                  new JFXX segment will be put
+*               new_JFXX_array - a JFXX information array in the same format as
+*                                from get_JFXX, to create the new segment
+*
+* Returns:      jpeg_header_data - the JPEG header data array with the new
+*                                  JFXX segment added
+*
+******************************************************************************/
+
+function put_JFXX( $jpeg_header_data, $new_JFXX_array )
+{
+        // pack the JFXX data into its proper format for a JPEG file
+        $packed_data = pack( 'a5Ca*',"JFXX\x00", $new_JFXX_array['Extension_Code'], $new_JFXX_array['ThumbData'] );
+
+        $JFIF_pos = -1;
+
+        //Cycle through the header segments
+        for( $i = 0; $i < count( $jpeg_header_data ); $i++ )
+        {
+                // If we find an APP0 header,
+                if ( strcmp ( $jpeg_header_data[$i]['SegName'], "APP0" ) == 0 )
+                {
+                        // And if it has the JFXX label,
+                        if( strncmp ( $jpeg_header_data[$i]['SegData'], "JFXX\x00", 5) == 0 )
+                        {
+                                // Found a preexisting JFXX block - Replace it with the new one and return.
+                                $jpeg_header_data[$i]['SegData'] = $packed_data;
+                                return $jpeg_header_data;
+                        }
+
+                        // if it has the JFIF label,
+                        if( strncmp ( $jpeg_header_data[$i][SegData], "JFIF\x00", 5) == 0 )
+                        {
+                                // Found a preexisting JFIF block - Mark it in case we need to insert the JFXX after it
+                                $JFIF_pos = $i;
+                        }
+                }
+        }
+
+
+        // No preexisting JFXX block found
+
+        // Check if a JFIF segment was found,
+        if ( $JFIF_pos !== -1 )
+        {
+                // A pre-existing JFIF segment was found,
+                // insert the new JFXX segment after it.
+                array_splice($jpeg_header_data, $JFIF_pos +1 , 0, array ( array(        "SegType" => 0xE0,
+                                                                                        "SegName" => "APP0",
+                                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
+                                                                                        "SegData" => $packed_data ) ) );
+
+        }
+        else
+        {
+                // No pre-existing JFIF segment was found,
+                // insert a new JFIF and the new JFXX segment at the start of the array.
+
+                // Insert new JFXX segment
+                array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
+                                                                        "SegName" => "APP0",
+                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
+                                                                        "SegData" => $packed_data ) ) );
+
+                // Create a new JFIF to be inserted at the start of
+                // the array, with generic values
+                $packed_data = pack( 'a5CCCnnCCa*',"JFIF\x00", 1, 2, 1, 72, 72, 0, 0, "" );
+
+                array_splice($jpeg_header_data, 0 , 0, array( array(   "SegType" => 0xE0,
+                                                                        "SegName" => "APP0",
+                                                                        "SegDesc" => $GLOBALS[ "JPEG_Segment_Descriptions" ][ 0xE0 ],
+                                                                        "SegData" => $packed_data ) ) );
+        }
+
+
+        return $jpeg_header_data;
+}
+
+/******************************************************************************
+* End of Function:     put_JFIF
+******************************************************************************/
+
+
+
+/******************************************************************************
+*
+* Function:     Interpret_JFXX_to_HTML
+*
+* Description:  Generates html showing the JFXX thumbnail contained in
+*               a JFXX data array, as retrieved with get_JFXX
+*
+* Parameters:   JFXX_array - a JFXX information array in the same format as
+*                            from get_JFXX, to create the new segment
+*               filename - the name of the JPEG file being processed ( used
+*                          by the script which displays the JFXX thumbnail)
+*
+* Returns:      output - the Html string
+*
+******************************************************************************/
+
+function Interpret_JFXX_to_HTML( $JFXX_array, $filename )
+{
+        $output = "";
+        if ( $JFXX_array !== FALSE )
+        {
+                $output .= "<H2 class=\"JFXX_Main_Heading\">Contains JPEG File Interchange Extension Format  (JFXX) Thumbnail</H2>\n";
+                switch ( $JFXX_array['Extension_Code'] )
+                {
+                        case 0x10 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is JPEG Encoded</p>\n";
+
+                                        // Change: as of version 1.11 - Changed to make thumbnail link portable across directories
+                                        // Build the path of the thumbnail script and its filename parameter to put in a url
+                                        $link_str = get_relative_path( dirname(__FILE__) . "/get_JFXX_thumb.php" , getcwd ( ) );
+                                        $link_str .= "?filename=";
+                                        $link_str .= get_relative_path( $filename, dirname(__FILE__) );
+
+                                        // Add thumbnail link to html
+                                        $output .= "<a class=\"JFXX_Thumbnail_Link\" href=\"$link_str\"><img  class=\"JFXX_Thumbnail\" src=\"$link_str\"></a>\n";
+                                        break;
+                        case 0x11 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 1 byte/pixel</p>\n";
+                                        $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n";
+                                        break;
+                        case 0x13 :     $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded 3 bytes/pixel</p>\n";
+                                        $output .= "<p class=\"JFXX_Text\">Thumbnail Display Not Implemented Yet</p>\n";
+                                        break;
+                        default :       $output .= "<p class=\"JFXX_Text\">JFXX Thumbnail is Encoded with Unknown format</p>\n";
+                                        break;
+
+                        // TODO: Implement JFXX one and three bytes per pixel thumbnail decoding
+                }
+
+        }
+
+        return $output;
+
+}
+
+/******************************************************************************
+* End of Function:     Interpret_JFXX_to_HTML
+******************************************************************************/
+
+
+
+
+?>
\ Kein Zeilenumbruch am Dateiende.
===================================================================
RCS file: ./RCS/index.php,v
retrieving revision 1.1
diff -u -r1.1 ./index.php
--- ./index.php	2010/09/24 20:16:31	1.1
+++ ./index.php	2011/09/04 21:16:42
@@ -18,7 +18,7 @@
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
-*  $Id: index.php,v 1.1 2010/09/24 20:16:31 root Exp $
+*  $Id: index.php,v 1.13 2011/09/04 15:31:44 root Exp $
 *
 */
 
@@ -94,6 +94,7 @@
 require_once INCLUDE_DIR . 'functions_graphical.inc.php';
 
 
+
 /***************************************************
 ****  Configuration  Initialization & Validation ***
 ***************************************************/
@@ -145,6 +146,41 @@
 else die("ERROR, Please choose either 'mysql' or 'file' as database type in your config file");
 
 
+
+/* PEL JPEG: */
+#
+require_once('./PEL/PelJpeg.php');
+#include './PEL/PelJpeg.php';
+require_once('./PEL/PelIfd.php');
+#include './PEL/PelIfd.php';
+
+if(isset($dir)){
+  $old_dir=$dir;
+} else {
+  unset($olddir);
+}
+# The PHP JPEG Metadata Toolkit
+$Toolkit_Dir = "./PHP_JPEG_Metadata_Toolkit_1.11/";     // Ensure dir name includes trailing slash
+
+// Hide any unknown EXIF tags
+$GLOBALS['HIDE_UNKNOWN_TAGS'] = TRUE;
+
+include $Toolkit_Dir . 'Toolkit_Version.php';          // Change: added as of version 1.11
+include $Toolkit_Dir . 'JPEG.php';                     // Change: Allow this example file to be easily relocatable - as of version 1.11
+include $Toolkit_Dir . 'JFIF.php';
+include $Toolkit_Dir . 'PictureInfo.php';
+include $Toolkit_Dir . 'XMP.php';
+include $Toolkit_Dir . 'Photoshop_IRB.php';
+include $Toolkit_Dir . 'EXIF.php';
+include $Toolkit_Dir . 'Photoshop_File_Info.php';
+
+if(isset($olddir)){
+  $dir=$olddir;
+} else {
+  $dir="";
+}
+
+
 /**********************************************
 ****        Error Handler init              ***
 **********************************************/
@@ -394,7 +430,7 @@
         // Registering $varname in the global scope if not a COOKIE
         if (!isset($_COOKIE[$varname])) {
 
-            // trigger_error("DEBUG: Registering \$".$varname." in the global scope", WARNING);
+            trigger_error("DEBUG: Registering \$".$varname." in the global scope", WARNING);
             $$varname=$value;
 
         } else {
@@ -515,6 +551,12 @@
 // Assign a value to $dir if not done already
 if($display && (dirname($display) != ".")) $dir=dirname($display);
 
+#print("<pre>
+#");
+#print_r($dir);
+#print("
+#</pre>");
+
 // Directory name NEED a trailing slash !
 if($dir && substr($dir,-1)!='/') $dir.='/';
 
@@ -672,6 +714,15 @@
 
     // NB: As we use the user input validation now, we won't re-check the validity of the input
 
+    trigger_error('DEBUG: --MARK-- before geotag', DEBUG);
+    trigger_error('DEBUG: --MARK-- lat:'.$lat.' lon:'.$lon, DEBUG);
+    if ($admin && $display && $lat!=0 && $lon!=0) {#
+    trigger_error('DEBUG: --MARK-- lat:'.$lat.' lon:'.$lon, DEBUG);
+        geotag_image($config['pictures_dir'].$display, 
+                     $lat, $lon, $address_txt);
+    }
+    trigger_error('DEBUG: --MARK-- after geotag', DEBUG);
+
     if ($admin && $display && $rotatepic) {
 
         // Get the rotation value (1, 2 or 3)
@@ -686,7 +737,7 @@
     }
 
     // pic delete
-    if($updpic=="del"&&$admin) {
+if($updpic=="del"&&$admin) {
         delete_pic($display);
         //jump back to the directory after deleting the pic
         $dir=dirname($display);
@@ -836,7 +887,7 @@
 
     $picinfo=getimagesize($phpGraphyNaming->getThumbFullPath($previewpic));
     $picinfo['title'] = get_title($previewpic);
-    if ($_SERVER['HTTPS'] == on) $proto == 'https'; else $proto='http';
+    if ($_SERVER['HTTPS'] == on) $proto = 'https'; else $proto='http';
 
     header("Content-type: application/x-javascript");
     $html = "var phpg_txt = '';\n";
@@ -1474,7 +1525,8 @@
     $tpl->assign("txt_previous_page", $txt_previous_page );
     $tpl->assign("txt_next_page", $txt_next_page );
 
-    $tpl->assign('page_uri', 'http://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
+    # $tpl->assign('page_uri', 'http://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
+    $tpl->assign('page_uri', $proto.'://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
 
     $tpl->display("top-and-last".(isset($rss)?'-rss':'').".tpl");
     if(!isset($rss)) include CURRENT_THEME_DIR.FOOTER_FILE;
@@ -1525,7 +1577,7 @@
     $tpl->assign("txt_previous_page", $txt_previous_page );
     $tpl->assign("txt_next_page", $txt_next_page );
 
-    $tpl->assign('page_uri', 'http://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
+    $tpl->assign('page_uri', $proto.'://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
 
     $tpl->display("top-and-last".(isset($rss)?'-rss':'').".tpl");
     if(!isset($rss)) include CURRENT_THEME_DIR.FOOTER_FILE;
@@ -1574,7 +1626,7 @@
     $tpl->assign("txt_previous_page", $txt_previous_page );
     $tpl->assign("txt_next_page", $txt_next_page );
 
-    $tpl->assign('page_uri', 'http://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
+    $tpl->assign('page_uri', $proto.'://'.$_SERVER['SERVER_NAME'].SCRIPT_NAME);
 
     $tpl->display("top-and-last".(isset($rss)?'-rss':'').".tpl");
     if(!isset($rss)) include CURRENT_THEME_DIR.FOOTER_FILE;
@@ -2018,6 +2070,23 @@
             $error_handler->restoreDisplay();
         }
 
+        $geopics=array();
+        $navmap = "<script type=\"text/javascript\">
+var myOptions = {
+    zoom: 7,
+    center: new google.maps.LatLng(0,0),
+    mapTypeId: google.maps.MapTypeId.HYBRID
+};
+
+
+var geomarkers=[];
+var latlng=[];
+var map;
+var latlngbounds = new google.maps.LatLngBounds( );
+
+function initMap(){
+map = new google.maps.Map(document.getElementById(\"navmap\"), myOptions);
+";
         // Create another array from ordered_files with pictures details
         if ($thumb_matrix) {
         foreach($thumb_matrix as $key1 => $table_row) {
@@ -2080,7 +2149,29 @@
                 } else {
                     $url = $base_images_dir.'blank.gif';
                 }
-
+                // get coords if available:
+                if (preg_match("/jpe?g$/i",$filename)) {
+#print_r($config['pictures_dir'].$dir.$filename);
+                   $exif_header=get_exif_data($config['pictures_dir'].$dir.$filename);
+                   if($exif_header['Exif.Latitude']!="unknown"){
+                     $geopics[$filename]=array();
+                     $geopics[$filename]['latitude'] = $exif_header['Exif.Latitude'];
+                     preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$geopics[$filename]['latitude'],$lat); 
+                     $geopics[$filename]['latitude_dec'] =$lat[1]+$lat[2]/60+$lat[3]/60/60; 
+                     $geopics[$filename]['longitude'] = $exif_header['Exif.Longitude'];
+                     preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$geopics[$filename]['longitude'],$lon); 
+                     $geopics[$filename]['longitude_dec'] =$lon[1]+$lon[2]/60+$lon[3]/60/60; 
+                     $navmap .= "npos=latlng.push(new google.maps.LatLng(".$geopics[$filename]['latitude_dec'].",".$geopics[$filename]['longitude_dec']."));
+";
+                     $navmap .= "
+    tmpmarker=new google.maps.Marker({position: latlng[npos-1], map: map, title: \"".$filename."\", draggable: false});
+    google.maps.event.addListener(tmpmarker, 'click', function() {
+      window.location=\"?display=".rawurlencode($dir.$filename)."\";
+    });
+    geomarkers.push(tmpmarker);
+";
+                   }
+                }
                 // Option to select thumb as directory cover
                 if ($pgFileTypes->isImage($filename) || $pgFileTypes->isVideo($filename)) {
 
@@ -2211,9 +2302,20 @@
         }
 
         $pager = $html;
-
+#print_r($geopics);
+# TODO: generate markers array
     }
     // END - PAGER
+        $navmap .= "
+for ( var i = 0; i < latlng.length; i++ )
+{
+  latlngbounds.extend( latlng[ i ] );
+}
+//map.setCenter( latlngbounds.getCenter( ), map.getBoundsZoomLevel( latlngbounds ) );
+map.fitBounds(latlngbounds);
+}
+</script>";//"NAVMAP:<pre>".implode('', $geopics)."</pre>";
+
 
 // END - BROWSE
 
@@ -2264,6 +2366,28 @@
             if ($config['use_iptc']) {
                // Load IPTC header
                $iptc_header=get_iptc_data($config['pictures_dir'].$display);
+
+		# FIXME: use primarily get_jpeg_header_data ... instead of iptc stuff!!
+		$jpeg_header_data = get_jpeg_header_data( $config['pictures_dir'].$display );
+		$exif_array=get_EXIF_jpeg($config['pictures_dir'].$display);
+		$xmp_array=read_XMP_array_from_text(get_XMP_text($jpeg_header_data));
+		$IRB_array=get_Photoshop_IRB($jpeg_header_data);
+		$PS_fileinfo=get_photoshop_file_info($exif_array,$xmp_array,$IRB_array);
+		#print("<pre>
+		#");
+		#print_r($exif_array);
+		#print_r($xmp_array);
+		#print_r($IRB_array);
+		#print_r($PS_fileinfo);
+		#print("
+		#</pre>");
+		if(!$iptc_header["Iptc.City"]){
+		   $iptc_header["Iptc.City"]=$PS_fileinfo["city"];
+		} 
+		if(!$iptc_header["Iptc.CountryName"]){
+		   $iptc_header["Iptc.CountryName"]=$PS_fileinfo["country"];
+		} 
+
             }
 
 
@@ -2282,11 +2406,53 @@
                 if ($iptc_header) $picture['metadata_found'] = 1; else $picture['metadata_found'] = 0;
             }
 
+            $picture['latitude'] = $exif_header['Exif.Latitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$picture['latitude'],$lat); 
+            $picture['latitude_dec'] =$lat[1]+$lat[2]/60+$lat[3]/60/60; 
+            $picture['longitude'] = $exif_header['Exif.Longitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$picture['longitude'],$lon); 
+            $picture['longitude_dec'] =$lon[1]+$lon[2]/60+$lon[3]/60/60; 
+
         // Picture Naviguation (Previous / Up / Next)
         // TODO: Handle this at the template level
         $html = '';
 
-        if($i!=0) $html .= '<a href="?display='.rawurlencode($dir.$files[$i-1]).'">'.$txt_previous_image.'</a>';
+        $lastpicthumb ='';
+        $last_picture_longitude_dec='';
+        $last_picture_latitude_dec='';
+        $next_picture_longitude_dec='';
+        $next_picture_latitude_dec='';
+
+        if($i!=0) {$html .= '<a href="?display='.rawurlencode($dir.$files[$i-1]).'">'.$txt_previous_image.'</a>';
+          $last_exif_header=get_exif_data($config['pictures_dir'].$dir.$files[$i-1]);
+            $last_picture_latitude = $last_exif_header['Exif.Latitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$last_picture_latitude,$last_lat); 
+            $last_picture_latitude_dec =$last_lat[1]+$last_lat[2]/60+$last_lat[3]/60/60; 
+            $last_picture_longitude = $last_exif_header['Exif.Longitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$last_picture_longitude,$last_lon); 
+            $last_picture_longitude_dec =$last_lon[1]+$last_lon[2]/60+$last_lon[3]/60/60; 
+
+          $lastpicinfo=getimagesize($phpGraphyNaming->getThumbFullPath($dir.$files[$i-1]));
+           $prot='';
+           if ($_SERVER['HTTPS'] == on) $prot = 'https'; else $prot='http';
+          $lastpicthumb .= '<img src="'.$prot.'://'.$_SERVER['HTTP_HOST'].SCRIPT_NAME.'?previewpic='.rawurlencode($dir.$files[$i-1]).'" width="'.$lastpicinfo[0].'" height="'.$lastpicinfo[1].'" alt="'.$last_picture_latitude_dec.','.$last_picture_longitude_dec.'" />';
+
+        }
+        if($i!=$nb_files-1) {$html .= '<a href="?display='.rawurlencode($dir.$files[$i+1]).'">'.$txt_next_image.'</a>';
+          $next_exif_header=get_exif_data($config['pictures_dir'].$dir.$files[$i+1]);
+            $next_picture_latitude = $next_exif_header['Exif.Latitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$next_picture_latitude,$next_lat); 
+            $next_picture_latitude_dec =$next_lat[1]+$next_lat[2]/60+$next_lat[3]/60/60; 
+            $next_picture_longitude =   $next_exif_header['Exif.Longitude'];
+            preg_match("/^([0-9.]*)  ([0-9.]*)' ([0-9.]*)''$/",$next_picture_longitude,$next_lon); 
+            $next_picture_longitude_dec =$next_lon[1]+$next_lon[2]/60+$next_lon[3]/60/60; 
+
+          $nextpicinfo=getimagesize($phpGraphyNaming->getThumbFullPath($dir.$files[$i+1]));
+           $prot='';
+           if ($_SERVER['HTTPS'] == on) $prot = 'https'; else $prot='http';
+          $nextpicthumb .= '<img src="'.$prot.'://'.$_SERVER['HTTP_HOST'].SCRIPT_NAME.'?previewpic='.rawurlencode($dir.$files[$i+1]).'" width="'.$nextpicinfo[0].'" height="'.$nextpicinfo[1].'" alt="'.$next_picture_latitude_dec.','.$next_picture_longitude_dec.'" />';
+
+        }
         $startpic=0;
         while ($i+1>$startpic+($config['nb_thumbs_max'])) { $startpic=$startpic+$config['nb_thumbs_max']; }
         $html .= ' <a href="?dir='.rawurlencode($dir).'&amp;startpic='.$startpic.'">'.$txt_back_dir.'</a>';
@@ -2306,6 +2472,9 @@
 
         if ($admin) {
 
+            $gps_form = '<form name="gps_form" method="GET" action="'.basename(SCRIPT_NAME).'">';
+            $gps_form .=  '<input type=hidden name=display value="'.$display.'" />';
+
             $html = '<fieldset id="adminpicture">
                 <legend>'.$txt_admin['picture settings'].'</legend>
                 <form name="picupdateform" action="'.basename(SCRIPT_NAME).'">
@@ -2439,6 +2608,7 @@
 
             $picture['metadata_array'] = $metadata_safe;
             $txt['show_me_more'] = $txt_show_me_more;
+            $txt['toggle_map'] = $txt_toggle_map;
 
            }
 
@@ -2483,6 +2653,12 @@
     $tpl->assign('nb_dirs', $nb_dirs_txt);
     $tpl->assign('nb_files', $nb_files_txt);
     $tpl->assign('navbar_menu', $navbar_menu);
+    $tpl->assign('lastpicthumb', $lastpicthumb);
+    $tpl->assign('last_picture_longitude_dec', $last_picture_longitude_dec);
+    $tpl->assign('last_picture_latitude_dec',  $last_picture_latitude_dec);
+    $tpl->assign('nextpicthumb', $nextpicthumb);
+    $tpl->assign('next_picture_longitude_dec', $next_picture_longitude_dec);
+    $tpl->assign('next_picture_latitude_dec',  $next_picture_latitude_dec);
     $tpl->assign('txt_last_commented', $txt_last_commented);
     $tpl->assign('txt_top_rated', $txt_top_rated);
     $tpl->assign('txt_last_added', $txt_last_added);
@@ -2511,6 +2687,7 @@
 
 
         $tpl->assign('pager', $pager);
+        $tpl->assign('navmap', $navmap);
 
         $tpl->assign('txt', $txt);
 
@@ -2523,6 +2700,7 @@
         $tpl->assign('base_images_dir', $base_images_dir);
         $tpl->assign('picnavbar', $picnavbar);
         $tpl->assign('adminpicturebox', $adminpicturebox);
+        $tpl->assign('gps_form', $gps_form);
         $tpl->assign('rating', $rating);
         $tpl->assign('comments', $comments);
         $tpl->assign('use_direct_urls', $config['use_direct_urls']);

