source: extensions/AMetaData/JpegMetaData/Readers/JpegReader.class.php @ 4972

Last change on this file since 4972 was 4972, checked in by grum, 14 years ago

Canon camera's maker note implementation + extended Xmp segments managed

  • Property svn:executable set to *
File size: 9.3 KB
Line 
1<?php
2/*
3 * --:: JPEG MetaDatas ::-------------------------------------------------------
4 *
5 *  Author    : Grum
6 *   email    : grum at piwigo.org
7 *   website  : http://photos.grum.fr
8 *
9 *   << May the Little SpaceFrog be with you ! >>
10 *
11 * +-----------------------------------------------------------------------+
12 * | JpegMetaData - a PHP based Jpeg Metadata manager                      |
13 * +-----------------------------------------------------------------------+
14 * | Copyright(C) 2010  Grum - http://www.grum.fr                          |
15 * +-----------------------------------------------------------------------+
16 * | This program is free software; you can redistribute it and/or modify  |
17 * | it under the terms of the GNU General Public License as published by  |
18 * | the Free Software Foundation                                          |
19 * |                                                                       |
20 * | This program is distributed in the hope that it will be useful, but   |
21 * | WITHOUT ANY WARRANTY; without even the implied warranty of            |
22 * | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
23 * | General Public License for more details.                              |
24 * |                                                                       |
25 * | You should have received a copy of the GNU General Public License     |
26 * | along with this program; if not, write to the Free Software           |
27 * | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
28 * | USA.                                                                  |
29 * +-----------------------------------------------------------------------+
30 *
31 *
32 * -----------------------------------------------------------------------------
33 *
34 * The JpegReader is the class for reading a Jpeg file
35 *
36 * Datas provided by this class are less friendly than data provided by the
37 * JpegMetaData class, because here you have to know the Jpeg format to
38 * understand how to exploit datas
39 *
40 * -----------------------------------------------------------------------------
41 *
42 * .. Notes ..
43 *
44 * The JpegReader class use a dedicated AppMarkerSegmentReader class rather than
45 * the php "getimagesize()" function because the php function returns an
46 * associative array of markers like ("APP1" => "......", "APPx" => "......")
47 *
48 * Typically a jpeg file contains XMP data inside an APP1 marker, like the exif
49 * codes. So, when using the php function, APP1 with XMP values isn't present
50 * (only the first APP1 segment found and used for EXIF is present)
51 *
52 *
53 * A Jpeg file normally begins with the 2 bytes FFD8 (the SOI marker) and ends
54 * with the 2 bytes FFD9 (the EOI marker)
55 * Some file have extra data after the EOI marker ; for now, the class can't
56 * manage this kind of file.
57 *
58 * The class use the AppMarkerSegmentReader class to get the content of :
59 *  - APP1 marker  => Exif, XMP
60 *  - APP13 marker => Iptc
61 *
62 * This class provides theses public functions :
63 *  - load
64 *  - getFileName
65 *  - isLoaded
66 *  - isValid
67 *  - countAppMarkerSegments
68 *  - getAppMarkerSegments
69 *
70 *  Read the code for help
71 *
72 * -----------------------------------------------------------------------------
73 */
74
75  require_once(JPEG_METADATA_DIR."Readers/AppMarkerSegmentReader.class.php");
76
77  class JpegReader
78  {
79    /**
80     * start of image file tag
81     */
82    const JPEG_SOI = 0xFFD8;
83    /**
84     * end of image file tag
85     */
86    const JPEG_EOI = 0xFFD9;
87
88
89    private $fileName = "";
90    private $isLoaded = false;
91    private $isValid = false;
92    private $appMarkerSegmentReader = Array();
93
94    private $fileHandler = false;
95
96    /**
97     * the constructor need an optional filename
98     *
99     * if no filename is given, you can use the "load" function after the object
100     * is instancied
101     *
102     * @Param String $fileName (optional)
103     *
104     */
105    function __construct($fileName = "")
106    {
107      if($fileName!="") $this->load($fileName);
108    }
109
110    function __destruct()
111    {
112      unset($this->appMarkerSegmentReader);
113      unset($this->fileHandler);
114      unset($this->fileName);
115    }
116
117    /**
118     * load a file
119     *
120     * @Param String $fileName
121     *
122     */
123    public function load($fileName)
124    {
125      /*
126       * initialize values
127       *
128       * if the file isn't a valid file, theses properties will not be in an
129       * uncertain state
130       */
131      $this->isLoaded=false;
132      $this->fileName="";
133      $this->isValid = false;
134      $this->appMarkerSegmentReader=Array();
135
136      /*
137       * if the file exist, try to open it
138       * determine if the file is a valid JPEG file
139       *
140       * if the file is valid, read the markers and close the file
141       */
142      if(file_exists($fileName))
143      {
144        $this->fileName=$fileName;
145
146        $this->fileHandler = fopen($fileName, "r");
147        if($this->fileHandler)
148        {
149          $this->isValid = $this->isAValidFile();
150
151          if($this->isValid)
152          {
153            $this->readAppMarkerSegments();
154            $this->processSpecialAppMarkerSegments();
155            $this->isLoaded=true;
156          }
157
158          fclose($this->fileHandler);
159          return(true);
160        }
161        return(false);
162      }
163      return(false);
164    }
165
166    /**
167     *
168     * this function try to see if the given file is a valid jpg file (the file
169     * must begins with the SOI and ends with the EOI)
170     *
171     */
172    protected function isAValidFile()
173    {
174      fseek($this->fileHandler, -2, SEEK_END);
175      $header=fread($this->fileHandler, 2);
176      if(ConvertData::toUShort($header, BYTE_ORDER_BIG_ENDIAN) == self::JPEG_EOI)
177      {
178        /*
179         * the file ends with the good marker
180         */
181        fseek($this->fileHandler, 0);
182        $header=fread($this->fileHandler, 2);
183        if(ConvertData::toUShort($header, BYTE_ORDER_BIG_ENDIAN) == self::JPEG_SOI)
184        {
185          return(true);
186        }
187      }
188      return(false);
189    }
190
191    /**
192     *
193     * This function reads all the segment of the jpeg file
194     *
195     */
196    private function readAppMarkerSegments()
197    {
198      /*
199       * loop to read all markers
200       */
201      $offset=2;
202      while(true)
203      {
204        $marker=AppMarkerSegmentReader::read($this->fileHandler, $offset);
205        if($marker)
206        {
207          /*
208           * if there is a marker returned, push it on the markers array
209           */
210          $this->appMarkerSegmentReader[]=$marker;
211          $offset+=$marker->getLength();
212          unset($marker);
213        }
214        else
215        {
216          /*
217           * if there is no marker returned (end of file ?) stop markers loading
218           */
219          return(false);
220        }
221      }
222
223    }
224
225
226    /**
227     *
228     * This function process special APP marker segment
229     *
230     * At now, only the extendedXmp segment are managed
231     *
232     */
233    protected function processSpecialAppMarkerSegments()
234    {
235      /*
236       * process APP extendedXmp segment
237       *
238       * 1/ read all APP segment and find APP1/EXTENDEDXMP segments
239       * 2/ sort APP1/EXTENDEDXMP segments by UID+OFFSET
240       * 3/ merge segment data
241       * 4/ build a new AppMarkerSegmentReader (as an APP1_XMP segement)
242       */
243      $extendedXmp=Array();
244      foreach($this->appMarkerSegmentReader as $marker)
245      {
246        if($marker->getHeader()==AppMarkerSegmentReader::SEGMENT_APP1 and
247           $marker->getSubType()==AppMarkerSegmentReader::APP1_EXTENDEDXMP)
248        {
249          $extendedXmp[]=$marker->getData();
250        }
251      }
252      usort($extendedXmp, Array(&$this, "sortExtendedXmp"));
253
254      $xmp="";
255      foreach($extendedXmp as $marker)
256      {
257        $xmp.=$marker['data'];
258      }
259
260      $marker=new AppMarkerSegmentReader(
261        AppMarkerSegmentReader::SEGMENT_APP1,
262        0,
263        strlen($xmp),
264        AppMarkerSegmentReader::APP1_XMP,
265        "http://ns.adobe.com/xap/1.0/\x00".$xmp
266      );
267
268      if($marker)
269      {
270        /*
271         * if there is a marker returned, push it on the markers array
272         */
273        $this->appMarkerSegmentReader[]=$marker;
274        unset($marker);
275      }
276      unset($extendedXmp);
277      unset($xmp);
278    }
279
280    /**
281     * This function is used to sort the extendedXmp segments
282     */
283    protected function sortExtendedXmp($a, $b)
284    {
285      if (($a['uid'].$a['offset']) == ($b['uid'].$b['offset']))
286      {
287        return(0);
288      }
289      return((($a['uid'].$a['offset']) < ($b['uid'].$b['offset'])) ? -1 : 1);
290    }
291
292
293    /**
294     *
295     * returns the filename given to the object
296     *
297     */
298    public function getFileName()
299    {
300      return($this->fileName);
301    }
302
303    /**
304     *
305     * returns true if the given jpeg file has been succesfully loaded
306     *
307     */
308    public function isLoaded()
309    {
310      return($this->isLoaded);
311    }
312
313    /**
314     *
315     * returns true if the given jpeg file is a valid file
316     *
317     */
318    public function isValid()
319    {
320      return($this->isValid);
321    }
322
323    /**
324     *
325     * returns the number od segments loaded
326     *
327     */
328    public function countAppMarkerSegments()
329    {
330      return(count($this->appMarkerSegmentReader));
331    }
332
333    /**
334     *
335     * returns an array of AppMarkerSegments
336     *
337     * see the AppMarkerSegmentsReader.class.php for more informations
338     *
339     */
340    public function getAppMarkerSegments()
341    {
342      return($this->appMarkerSegmentReader);
343    }
344
345  }
346
347
348 ?>
Note: See TracBrowser for help on using the repository browser.