Source for file PelJpeg.php

Documentation is available at PelJpeg.php

  1. <?php
  2.  
  3. /*  PEL: PHP Exif Library.  A library with support for reading and
  4.  *  writing all Exif headers in JPEG and TIFF images using PHP.
  5.  *
  6.  *  Copyright (C) 2004, 2005, 2006, 2007  Martin Geisler.
  7.  *
  8.  *  This program is free software; you can redistribute it and/or modify
  9.  *  it under the terms of the GNU General Public License as published by
  10.  *  the Free Software Foundation; either version 2 of the License, or
  11.  *  (at your option) any later version.
  12.  *
  13.  *  This program is distributed in the hope that it will be useful,
  14.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *  GNU General Public License for more details.
  17.  *
  18.  *  You should have received a copy of the GNU General Public License
  19.  *  along with this program in the file COPYING; if not, write to the
  20.  *  Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  21.  *  Boston, MA 02110-1301 USA
  22.  */
  23.  
  24. /* $Id$ */
  25.  
  26.  
  27. /**
  28.  * Classes representing JPEG data.
  29.  *
  30.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  31.  * @version $Revision$
  32.  * @date $Date$
  33.  * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL)
  34.  * @package PEL
  35.  */
  36.  
  37. /**#@+ Required class definitions. */
  38. require_once('PelJpegComment.php');
  39. require_once('PelJpegContent.php');
  40. require_once('PelDataWindow.php');
  41. require_once('PelJpegMarker.php');
  42. require_once('PelException.php');
  43. require_once('PelExif.php');
  44. require_once('Pel.php');
  45. /**#@-*/
  46.  
  47.  
  48. /**
  49.  * Exception thrown when an invalid marker is found.
  50.  *
  51.  * This exception is thrown when PEL expects to find a {@link }
  52.  * PelJpegMarker} and instead finds a byte that isn't a known marker.
  53.  *
  54.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  55.  * @package PEL
  56.  * @subpackage Exception
  57.  */
  58.  
  59.   /**
  60.    * Construct a new invalid marker exception.
  61.    *
  62.    * The exception will contain a message describing the error,
  63.    * including the byte found and the offset of the offending byte.
  64.    *
  65.    * @param int the byte found.
  66.    *
  67.    * @param int the offset in the data.
  68.    */
  69.   function __construct($marker$offset{
  70.     parent::__construct('Invalid marker found at offset %d: 0x%2X',
  71.                         $offset$marker);
  72.   }
  73. }
  74.  
  75. /**
  76.  * Class for handling JPEG data.
  77.  *
  78.  * The {@link PelJpeg} class defined here provides an abstraction for
  79.  * dealing with a JPEG file.  The file will be contain a number of
  80.  * sections containing some {@link PelJpegContent content} identified
  81.  * by a {@link PelJpegMarker marker}.
  82.  *
  83.  * The {@link getExif()} method is used get hold of the {@link }
  84.  * PelJpegMarker::APP1 APP1} section which stores Exif data.  So if
  85.  * the name of the JPEG file is stored in $filename, then one would
  86.  * get hold of the Exif data by saying:
  87.  *
  88.  * <code>
  89.  * $jpeg = new PelJpeg($filename);
  90.  * $exif = $jpeg->getExif();
  91.  * $tiff = $exif->getTiff();
  92.  * $ifd0 = $tiff->getIfd();
  93.  * $exif = $ifd0->getSubIfd(PelIfd::EXIF);
  94.  * $ifd1 = $ifd0->getNextIfd();
  95.  * </code>
  96.  *
  97.  * The $idf0 and $ifd1 variables will then be two {@link PelTiff TIFF}
  98.  * {@link PelIfd Image File Directories}, in which the data is stored
  99.  * under the keys found in {@link PelTag}.
  100.  *
  101.  * Should one have some image data (in the form of a {@link }
  102.  * PelDataWindow}) of an unknown type, then the {@link }
  103.  * PelJpeg::isValid()} function is handy: it will quickly test if the
  104.  * data could be valid JPEG data.  The {@link PelTiff::isValid()}
  105.  * function does the same for TIFF images.
  106.  *
  107.  * @author Martin Geisler <mgeisler@users.sourceforge.net>
  108.  * @package PEL
  109.  */
  110. class PelJpeg {
  111.  
  112.   /**
  113.    * The sections in the JPEG data.
  114.    *
  115.    * A JPEG file is built up as a sequence of sections, each section
  116.    * is identified with a {@link PelJpegMarker}.  Some sections can
  117.    * occur more than once in the JPEG stream (the {@link }
  118.    * PelJpegMarker::DQT DQT} and {@link PelJpegMarker::DHT DTH}
  119.    * markers for example) and so this is an array of ({@link }
  120.    * PelJpegMarker}, {@link PelJpegContent}) pairs.
  121.    *
  122.    * The content can be either generic {@link PelJpegContent JPEG}
  123.    * content} or {@link PelExif Exif data}.
  124.    *
  125.    * @var array 
  126.    */
  127.   private $sections array();
  128.  
  129.   /**
  130.    * The JPEG image data.
  131.    *
  132.    * @var PelDataWindow 
  133.    */
  134.   private $jpeg_data null;
  135.  
  136.   /**
  137.    * Construct a new JPEG object.
  138.    *
  139.    * The new object will be empty unless an argument is given from
  140.    * which it can initialize itself. This can either be the filename
  141.    * of a JPEG image, a {@link PelDataWindow} object or a PHP image
  142.    * resource handle.
  143.    *
  144.    * New Exif data (in the form of a {@link PelExif} object) can be
  145.    * inserted with the {@link setExif()} method:
  146.    *
  147.    * <code>
  148.    * $jpeg = new PelJpeg($data);
  149.    * // Create container for the Exif information:
  150.    * $exif = new PelExif();
  151.    * // Now Add a PelTiff object with a PelIfd object with one or more
  152.    * // PelEntry objects to $exif... Finally add $exif to $jpeg:
  153.    * $jpeg->setExif($exif);
  154.    * </code>
  155.    *
  156.    * @param mixed the data that this JPEG. This can either be a
  157.    *  filename, a {@link PelDataWindow} object, or a PHP image resource
  158.    *  handle.
  159.    */
  160.   function __construct($data false{
  161.     if ($data === false)
  162.       return;
  163.  
  164.     if (is_string($data)) {
  165.       Pel::debug('Initializing PelJpeg object from %s'$data);
  166.       $this->loadFile($data);
  167.     elseif ($data instanceof PelDataWindow{
  168.       Pel::debug('Initializing PelJpeg object from PelDataWindow.');
  169.       $this->load($data);
  170.     elseif (is_resource($data&& get_resource_type($data== 'gd'{
  171.       Pel::debug('Initializing PelJpeg object from image resource.');
  172.       $this->load(new PelDataWindow($data));
  173.     else {
  174.       throw new PelInvalidArgumentException('Bad type for $data: %s'
  175.                                             gettype($data));
  176.     }
  177.   }
  178.  
  179.   /**
  180.    * Load data into a JPEG object.
  181.    *
  182.    * The data supplied will be parsed and turned into an object
  183.    * structure representing the image.  This structure can then be
  184.    * manipulated and later turned back into an string of bytes.
  185.    *
  186.    * This methods can be called at any time after a JPEG object has
  187.    * been constructed, also after the {@link appendSection()} has been
  188.    * called to append custom sections.  Loading several JPEG images
  189.    * into one object will accumulate the sections, but there will only
  190.    * be one {@link PelJpegMarker::SOS} section at any given time.
  191.    *
  192.    * @param PelDataWindow the data that will be turned into JPEG
  193.    *  sections.
  194.    */
  195.   function load(PelDataWindow $d{
  196.  
  197.     Pel::debug('Parsing %d bytes...'$d->getSize());
  198.  
  199.     /* JPEG data is stored in big-endian format. */
  200.     $d->setByteOrder(PelConvert::BIG_ENDIAN);
  201.     
  202.     /* Run through the data to read the sections in the image.  After
  203.      * each section is read, the start of the data window will be
  204.      * moved forward, and after the last section we'll terminate with
  205.      * no data left in the window. */
  206.     while ($d->getSize(0{
  207.       /* JPEG sections start with 0xFF. The first byte that is not
  208.        * 0xFF is a marker (hopefully).
  209.        */
  210.       for ($i 0$i 7$i++)
  211.         if ($d->getByte($i!= 0xFF)
  212.           break;
  213.  
  214.       $marker $d->getByte($i);
  215.  
  216.       if (!PelJpegMarker::isValid($marker))
  217.         throw new PelJpegInvalidMarkerException($marker$i);
  218.  
  219.       /* Move window so first byte becomes first byte in this
  220.        * section. */
  221.       $d->setWindowStart($i+1);
  222.  
  223.       if ($marker == PelJpegMarker::SOI || $marker == PelJpegMarker::EOI{
  224.         $content new PelJpegContent(new PelDataWindow());
  225.         $this->appendSection($marker$content);
  226.       else {
  227.         /* Read the length of the section.  The length includes the
  228.          * two bytes used to store the length. */
  229.         $len $d->getShort(02;
  230.         
  231.         Pel::debug('Found %s section of length %d',
  232.                    PelJpegMarker::getName($marker)$len);
  233.  
  234.         /* Skip past the length. */
  235.         $d->setWindowStart(2);
  236.  
  237.         if ($marker == PelJpegMarker::APP1{
  238.  
  239.           try {
  240.             $content new PelExif();
  241.             $content->load($d->getClone(0$len));
  242.           catch (PelInvalidDataException $e{
  243.             /* We store the data as normal JPEG content if it could
  244.              * not be parsed as Exif data. */
  245.             $content new PelJpegContent($d->getClone(0$len));
  246.           }
  247.  
  248.           $this->appendSection($marker$content);
  249.           /* Skip past the data. */
  250.           $d->setWindowStart($len);
  251.  
  252.         elseif ($marker == PelJpegMarker::COM{
  253.  
  254.           $content new PelJpegComment();
  255.           $content->load($d->getClone(0$len));
  256.           $this->appendSection($marker$content);
  257.           $d->setWindowStart($len);
  258.  
  259.         else {
  260.  
  261.           $content new PelJpegContent($d->getClone(0$len));
  262.           $this->appendSection($marker$content);
  263.           /* Skip past the data. */
  264.           $d->setWindowStart($len);
  265.           
  266.           /* In case of SOS, image data will follow. */
  267.           if ($marker == PelJpegMarker::SOS{
  268.             /* Some images have some trailing (garbage?) following the
  269.              * EOI marker.  To handle this we seek backwards until we
  270.              * find the EOI marker.  Any trailing content is stored as
  271.              * a PelJpegContent object. */
  272.  
  273.             $length $d->getSize();
  274.             while ($d->getByte($length-2!= 0xFF ||
  275.                    $d->getByte($length-1!= PelJpegMarker::EOI{
  276.               $length--;
  277.             }
  278.  
  279.             $this->jpeg_data $d->getClone(0$length-2);
  280.             Pel::debug('JPEG data: ' $this->jpeg_data->__toString());
  281.  
  282.             /* Append the EOI. */
  283.             $this->appendSection(PelJpegMarker::EOI,
  284.                                  new PelJpegContent(new PelDataWindow()));
  285.  
  286.             /* Now check to see if there are any trailing data. */
  287.             if ($length != $d->getSize()) {
  288.               Pel::maybeThrow(new PelException('Found trailing content ' .
  289.                                                'after EOI: %d bytes',
  290.                                                $d->getSize($length));
  291.               $content new PelJpegContent($d->getClone($length));
  292.               /* We don't have a proper JPEG marker for trailing
  293.                * garbage, so we just use 0x00... */
  294.               $this->appendSection(0x00$content);
  295.             }
  296.  
  297.             /* Done with the loop. */
  298.             break;
  299.           }
  300.         }
  301.       }
  302.     /* while ($d->getSize() > 0) */
  303.   }
  304.  
  305.  
  306.   /**
  307.    * Load data from a file into a JPEG object.
  308.    *
  309.    * @param string the filename.  This must be a readable file.
  310.    */
  311.   function loadFile($filename{
  312.     $this->load(new PelDataWindow(file_get_contents($filename)));
  313.   }
  314.   
  315.  
  316.   /**
  317.    * Set Exif data.
  318.    *
  319.    * Use this to set the Exif data in the image. This will overwrite
  320.    * any old Exif information in the image.
  321.    *
  322.    * @param PelExif the Exif data.
  323.    */
  324.   function setExif(PelExif $exif{
  325.     $app0_offset 1;
  326.     $app1_offset = -1;
  327.  
  328.     /* Search through all sections looking for APP0 or APP1. */
  329.     for ($i 0$i count($this->sections)$i++{
  330.       if ($this->sections[$i][0== PelJpegMarker::APP0{
  331.         $app0_offset $i;
  332.       elseif ($this->sections[$i][0== PelJpegMarker::APP1{
  333.         $app1_offset $i;
  334.         break;
  335.       }
  336.     }
  337.  
  338.     /* Store the Exif data at the appropriate place, either where the
  339.      * old Exif data was stored ($app1_offset) or right after APP0
  340.      * ($app0_offset+1). */
  341.     if ($app1_offset 0)
  342.       $this->sections[$app1_offset][1$exif;
  343.     else
  344.       $this->insertSection(PelJpegMarker::APP1$exif$app0_offset+1);
  345.   }
  346.  
  347.  
  348.   /**
  349.    * Get Exif data.
  350.    *
  351.    * Use this to get the @{link PelExif Exif data} stored.
  352.    *
  353.    * @return PelExif the Exif data found or null if the image has no
  354.    *  Exif data.
  355.    */
  356.   function getExif({
  357.     $exif $this->getSection(PelJpegMarker::APP1);
  358.     if ($exif instanceof PelExif)
  359.       return $exif;
  360.     else
  361.       return null;
  362.   }
  363.  
  364.  
  365.   /**
  366.    * Clear any Exif data.
  367.    *
  368.    * This method will only clear the first @{link PelJpegMarker::APP1}
  369.    * section found (there should normally be just one).
  370.    */
  371.   function clearExif({
  372.     for ($i 0$i count($this->sections)$i++{
  373.       if ($this->sections[$i][0== PelJpegMarker::APP1{
  374.         unset($this->sections[$i]);
  375.         return;
  376.       }
  377.     }
  378.   }
  379.  
  380.  
  381.   /**
  382.    * Append a new section.
  383.    *
  384.    * Used only when loading an image. If it used again later, then the
  385.    * section will end up after the @{link PelJpegMarker::EOI EOI
  386.    * marker} and will probably not be useful.
  387.    *
  388.    * Please use @{link setExif()} instead if you intend to add Exif
  389.    * information to an image as that function will know the right
  390.    * place to insert the data.
  391.    *
  392.    * @param PelJpegMarker the marker identifying the new section.
  393.    *
  394.    * @param PelJpegContent the content of the new section.
  395.    */
  396.   function appendSection($markerPelJpegContent $content{
  397.     $this->sections[array($marker$content);
  398.   }
  399.  
  400.  
  401.   /**
  402.    * Insert a new section.
  403.    *
  404.    * Please use @{link setExif()} instead if you intend to add Exif
  405.    * information to an image as that function will know the right
  406.    * place to insert the data.
  407.    *
  408.    * @param PelJpegMarker the marker for the new section.
  409.    *
  410.    * @param PelJpegContent the content of the new section.
  411.    *
  412.    * @param int the offset where the new section will be inserted ---
  413.    *  use 0 to insert it at the very beginning, use 1 to insert it
  414.    *  between sections 1 and 2, etc.
  415.    */
  416.   function insertSection($markerPelJpegContent $content$offset{
  417.     array_splice($this->sections$offset0array(array($marker$content)));
  418.   }
  419.  
  420.  
  421.   /**
  422.    * Get a section corresponding to a particular marker.
  423.    *
  424.    * Please use the {@link getExif()} if you just need the Exif data.
  425.    *
  426.    * This will search through the sections of this JPEG object,
  427.    * looking for a section identified with the specified {@link }
  428.    * PelJpegMarker marker}.  The {@link PelJpegContent content} will
  429.    * then be returned.  The optional argument can be used to skip over
  430.    * some of the sections.  So if one is looking for the, say, third
  431.    * {@link PelJpegMarker::DHT DHT} section one would do:
  432.    *
  433.    * <code>
  434.    * $dht3 = $jpeg->getSection(PelJpegMarker::DHT, 2);
  435.    * </code>
  436.    *
  437.    * @param PelJpegMarker the marker identifying the section.
  438.    *
  439.    * @param int the number of sections to be skipped.  This must be a
  440.    *  non-negative integer.
  441.    *
  442.    * @return PelJpegContent the content found, or null if there is no
  443.    *  content available.
  444.    */
  445.   function getSection($marker$skip 0{
  446.     foreach ($this->sections as $s{
  447.       if ($s[0== $marker)
  448.         if ($skip 0)
  449.           $skip--;
  450.         else
  451.           return $s[1];
  452.     }
  453.  
  454.     return null;        
  455.   }
  456.  
  457.  
  458.   /**
  459.    * Get all sections.
  460.    *
  461.    * @return array an array of ({@link PelJpegMarker}{@link }
  462.    *  PelJpegContent}) pairs.  Each pair is an array with the {@link }
  463.    *  PelJpegMarker} as the first element and the {@link }
  464.    *  PelJpegContent} as the second element, so the return type is an
  465.    *  array of arrays.
  466.    *
  467.    *  So to loop through all the sections in a given JPEG image do
  468.    *  this:
  469.    *
  470.    *  <code>
  471.    *  foreach ($jpeg->getSections() as $section) {
  472.    *    $marker = $section[0];
  473.    *    $content = $section[1];
  474.    *    // Use $marker and $content here.
  475.    *  }
  476.    *  </code>
  477.    *
  478.    *  instead of this:
  479.    *
  480.    *  <code>
  481.    *  foreach ($jpeg->getSections() as $marker => $content) {
  482.    *    // Does not work the way you would think...
  483.    *  }
  484.    *  </code>
  485.    *
  486.    *  The problem is that there could be several sections with the same
  487.    *  marker, and thus a simple associative array does not suffice.
  488.    */
  489.   function getSections({
  490.     return $this->sections;
  491.   }
  492.  
  493.  
  494.   /**
  495.    * Turn this JPEG object into bytes.
  496.    *
  497.    * The bytes returned by this method is ready to be stored in a file
  498.    * as a valid JPEG image. Use the {@link saveFile()} convenience
  499.    * method to do this.
  500.    *
  501.    * @return string bytes representing this JPEG object, including all
  502.    *  its sections and their associated data.
  503.    */
  504.   function getBytes({
  505.     $bytes '';
  506.  
  507.     foreach ($this->sections as $section{
  508.       $m $section[0];
  509.       $c $section[1];
  510.  
  511.       /* Write the marker */
  512.       $bytes .= "\xFF" PelJpegMarker::getBytes($m);
  513.       /* Skip over empty markers. */
  514.       if ($m == PelJpegMarker::SOI || $m == PelJpegMarker::EOI)
  515.         continue;
  516.  
  517.       $data $c->getBytes();
  518.       $size strlen($data2;
  519.       
  520.       $bytes .= PelConvert::shortToBytes($sizePelConvert::BIG_ENDIAN);
  521.       $bytes .= $data;
  522.       
  523.       /* In case of SOS, we need to write the JPEG data. */
  524.       if ($m == PelJpegMarker::SOS)
  525.         $bytes .= $this->jpeg_data->getBytes();
  526.     }
  527.  
  528.     return $bytes;
  529.  
  530.   }
  531.  
  532.  
  533.   /**
  534.    * Save the JPEG object as a JPEG image in a file.
  535.    *
  536.    * @param string the filename to save in. An existing file with the
  537.    *  same name will be overwritten!
  538.    */
  539.   function saveFile($filename{
  540.     file_put_contents($filename$this->getBytes());
  541.   }
  542.  
  543.  
  544.   /**
  545.    * Make a string representation of this JPEG object.
  546.    *
  547.    * This is mainly usefull for debugging.  It will show the structure
  548.    * of the image, and its sections.
  549.    *
  550.    * @return string debugging information about this JPEG object.
  551.    */
  552.   function __toString({
  553.     $str Pel::tra("Dumping JPEG data...\n");
  554.     for ($i 0$i count($this->sections)$i++{
  555.       $m $this->sections[$i][0];
  556.       $c $this->sections[$i][1];
  557.       $str .= Pel::fmt("Section %d (marker 0x%02X - %s):\n",
  558.                        $i$mPelJpegMarker::getName($m));
  559.       $str .= Pel::fmt("  Description: %s\n",
  560.                        PelJpegMarker::getDescription($m));
  561.       
  562.       if ($m == PelJpegMarker::SOI ||
  563.           $m == PelJpegMarker::EOI)
  564.         continue;
  565.       
  566.       if ($c instanceof PelExif{
  567.         $str .= Pel::tra("  Content    : Exif data\n");
  568.         $str .= $c->__toString("\n";
  569.       elseif ($c instanceof PelJpegComment{
  570.         $str .= Pel::fmt("  Content    : %s\n"$c->getValue());
  571.       else {
  572.         $str .= Pel::tra("  Content    : Unknown\n");
  573.       }
  574.     }
  575.  
  576.     return $str;
  577.   }
  578.  
  579.  
  580.   /**
  581.    * Test data to see if it could be a valid JPEG image.
  582.    *
  583.    * The function will only look at the first few bytes of the data,
  584.    * and try to determine if it could be a valid JPEG image based on
  585.    * those bytes.  This means that the check is more like a heuristic
  586.    * than a rigorous check.
  587.    *
  588.    * @param PelDataWindow the bytes that will be checked.
  589.    *
  590.    * @return boolean true if the bytes look like the beginning of a
  591.    *  JPEG image, false otherwise.
  592.    *
  593.    * @see PelTiff::isValid()
  594.    */
  595.   static function isValid(PelDataWindow $d{
  596.     /* JPEG data is stored in big-endian format. */
  597.     $d->setByteOrder(PelConvert::BIG_ENDIAN);
  598.     
  599.     for ($i 0$i 7$i++)
  600.       if ($d->getByte($i!= 0xFF)
  601.         break;
  602.     
  603.     return $d->getByte($i== PelJpegMarker::SOI;
  604.   }
  605.  
  606. }
  607.  
  608. ?>

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