source: extensions/AMetaData/JpegMetaData/Readers/AppMarkerSegmentReader.class.php @ 7511

Last change on this file since 7511 was 7511, checked in by grum, 13 years ago

Update JpegMetadata class to implement COM segment as a tag and keywords in "magic" tags
feature:1975, feature:1976

  • Property svn:executable set to *
File size: 28.1 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 AppMarkerSegmentReader is a class for reading the segment of a Jpeg file
35 *
36 * -----------------------------------------------------------------------------
37 *
38 * .. Notes ..
39 *
40 * The class can recognize many kind of segments, but the content is not
41 * analyzed for all of them.
42 *
43 * If you want to implement new functionnality to read segment, you must start
44 * here.
45 *
46 * Datas are exploited only for the segments :
47 *  - APP1  : for Exif IFDs & Xmp datas
48 *  - APP13 : for 8BIM Iptc blocks
49 *  - COM   : comments block
50 *
51 *
52 * This class provides theses public functions :
53 *  - (static) read
54 *  - (static) isValidMarkerHeader
55 *  - (static) markerName
56 *  - getHeader
57 *  - getOffset
58 *  - getLength
59 *  - getSubType
60 *  - dataLoaded
61 *  - getData
62 *
63 *  Read the code for help
64 *
65 * -----------------------------------------------------------------------------
66 *
67 */
68
69  require_once(JPEG_METADATA_DIR."Common/ConvertData.class.php");
70  require_once(JPEG_METADATA_DIR."Common/Data.class.php");
71
72  require_once(JPEG_METADATA_DIR."Readers/TiffReader.class.php");
73  require_once(JPEG_METADATA_DIR."Readers/XmpReader.class.php");
74  require_once(JPEG_METADATA_DIR."Readers/IptcReader.class.php");
75  require_once(JPEG_METADATA_DIR."Readers/ComReader.class.php");
76
77  class AppMarkerSegmentReader
78  {
79    const SEGMENT_HEAD  = 0xFF;
80    const SEGMENT_APP0  = 0xE0;
81    const SEGMENT_APP1  = 0xE1;
82    const SEGMENT_APP2  = 0xE2;
83    const SEGMENT_APP3  = 0xE3;
84    const SEGMENT_APP4  = 0xE4;
85    const SEGMENT_APP5  = 0xE5;
86    const SEGMENT_APP6  = 0xE6;
87    const SEGMENT_APP7  = 0xE7;
88    const SEGMENT_APP8  = 0xE8;
89    const SEGMENT_APP9  = 0xE9;
90    const SEGMENT_APP10 = 0xEA;
91    const SEGMENT_APP11 = 0xEB;
92    const SEGMENT_APP12 = 0xEC;
93    const SEGMENT_APP13 = 0xED;
94    const SEGMENT_APP14 = 0xEE;
95    const SEGMENT_APP15 = 0xEF;
96
97    const SEGMENT_SOF0  = 0xC0;  // Start Of Frame - Baseline DCT (huffman)
98    const SEGMENT_SOF1  = 0xC1;  // Start Of Frame - Extended sequential DCT (huffman)
99    const SEGMENT_SOF2  = 0xC2;  // Start Of Frame - Progressive DCT (huffman)
100    const SEGMENT_SOF3  = 0xC3;  // Start Of Frame - Spatial (Lossless) DCT (Huffman)
101    const SEGMENT_SOF5  = 0xC5;  // Start Of Frame - Differential sequential DCT (Huffman)
102    const SEGMENT_SOF6  = 0xC6;  // Start Of Frame - Progressive sequential DCT (Huffman)
103    const SEGMENT_SOF7  = 0xC7;  // Start Of Frame - Differential spatial DCT (Huffman)
104    const SEGMENT_SOF9  = 0xC9;  // Start Of Frame - Extended sequential DCT (arithmetic)
105    const SEGMENT_SOFA  = 0xCA;  // Start Of Frame - Progressive DCT (arithmetic)
106    const SEGMENT_SOFB  = 0xCB;  // Start Of Frame - Spatial (lossless) DCT (arithmetic)
107    const SEGMENT_SOFD  = 0xCD;  // Start Of Frame - Differential sequential DCT (arithmetic)
108    const SEGMENT_SOFE  = 0xCE;  // Start Of Frame - Differential progressive DCT (arithmetic)
109    const SEGMENT_SOFF  = 0xCF;  // Start Of Frame - Differential lossless (arithmetic)
110
111    const SEGMENT_DQT   = 0xDB;  // Define Quantization Table
112    const SEGMENT_DHT   = 0xC4;  // Define Huffman Tables
113    const SEGMENT_JPG   = 0xC8;  // Reserved for JPEG extensions
114    const SEGMENT_DAC   = 0xCC;  // Arithmetic Conditionning info
115    const SEGMENT_SOS   = 0xDA;  // Start Of Scan
116    const SEGMENT_DRI   = 0xDD;  // Define Restart Interval
117    const SEGMENT_COM   = 0xFE;  // Comment
118
119
120
121    const UNKNOWN          = "?";
122
123    /*
124     * APP0 SubTypes
125     */
126    const APP0_JFIF        = "JFIF";
127    const APP0_JFXX        = "JFXX";
128    const APP0_CIFF        = "CIFF";
129    const APP0_AVI1        = "AVI1";
130
131    /*
132     * APP1 SubTypes
133     */
134    const APP1_EXIF        = "EXIF";
135    const APP1_XMP         = "XMP";
136    const APP1_EXTENDEDXMP = "ExtendedXMP";
137
138    /*
139     * APP2 SubTypes
140     */
141    const APP2_ICCPROFILE  = "ICC Profile";
142    const APP2_FPXR        = "FlashPix";
143    const APP2_MPF         = "MPF";
144    const APP2_SAMSUNGLP   = "Samsung large preview";
145
146    /*
147     * APP3 SubTypes
148     */
149    const APP3_META        = "Meta";
150    const APP3_STIM        = "Stim";
151
152    /*
153     * APP4 SubTypes
154     */
155    const APP4_SCALADO     = "Scalado";
156
157    /*
158     * APP5 SubTypes
159     */
160    const APP5_RMETA       = "Ricoh Meta";
161
162    /*
163     * APP6 SubTypes
164     */
165    const APP6_EPPIM       = "EPPIM";
166    const APP6_NITF        = "NITF";
167
168    /*
169     * APP8 SubTypes
170     */
171    const APP8_SPIFF       = "SPIFF";
172
173    /*
174     * APP10 SubTypes
175     */
176    const APP10_COMMENT    = "Comment";
177
178    /*
179     * APP12 SubTypes
180     */
181    const APP12_PICTUREINFO = "PictureInfo";
182    const APP12_DUCKY      = "Ducky";
183
184    /*
185     * APP13 SubTypes
186     */
187    const APP13_PHOTOSHOP  = "Photoshop";
188    const APP13_ADOBECM    = "Adobe CM";
189
190    /*
191     * APP14 SubTypes
192     */
193    const APP14_ADOBE      = "Adobe";
194
195    /*
196     * APP15 SubTypes
197     */
198    const APP15_GRAPHICCONVERTER = "GraphicConverter";
199
200    /*
201     * Other SubTypes
202     */
203    const SOF0 = "Start Of Frame - Baseline DCT (huffman)";
204    const SOF1 = "Start Of Frame - Extended sequential DCT (huffman)";
205    const SOF2 = "Start Of Frame - Progressive DCT (huffman)";
206    const SOF3 = "Start Of Frame - Spatial (Lossless) DCT (Huffman)";
207    const SOF5 = "Start Of Frame - Differential sequential DCT (Huffman)";
208    const SOF6 = "Start Of Frame - Progressive sequential DCT (Huffman)";
209    const SOF7 = "Start Of Frame - Differential spatial DCT (Huffman)";
210    const SOF9 = "Start Of Frame - Extended sequential DCT (arithmetic)";
211    const SOFA = "Start Of Frame - Progressive DCT (arithmetic)";
212    const SOFB = "Start Of Frame - Spatial (lossless) DCT (arithmetic)";
213    const SOFD = "Start Of Frame - Differential sequential DCT (arithmetic)";
214    const SOFE = "Start Of Frame - Differential progressive DCT (arithmetic)";
215    const SOFF = "Start Of Frame - Differential lossless (arithmetic)";
216    const DHT  = "Define Huffman Tables";
217    const JPG  = "Reserved for JPEG extensions";
218    const DAC  = "Arithmetic Conditionning info";
219    const SOS  = "Start Of Scan";
220    const DRI  = "Define Restart Interval";
221    const COM  = "Comment";
222    const DQT  = "Define Quantization Table";
223
224
225
226    private $header = 0xFF;
227    private $offset = 0;
228    private $length = 0;
229    private $subType = "";
230    private $data = NULL;
231    private $dataLoaded = false;
232    private $workData = NULL;
233
234
235    /**
236     * try to read a marker at the designed offset
237     *
238     * @param handler $fileHandler : a handler on an opened and valid file
239     *
240     * @param ULong $offset : where start reading the segment in the file
241     *
242     * @return false if there is no marker at this offset, otherwise returns an
243     * instance of a new AppMarkerSegmentReader object
244     */
245    static public function read($fileHandler, $offset)
246    {
247      /* if end of file, return false */
248      if(feof($fileHandler)) return(false);
249
250      fseek($fileHandler, $offset);
251      $header=ConvertData::toUByte(fread($fileHandler, 1));
252
253      /*
254       * each segment header start with 0xFF
255       *
256       * if the reader byte equals 0xFF, read the next byte.
257       * the next byte must equals one of the defined segment headers (between
258       * the first and last valid segments)
259       *
260       * if the segment is valid, the next 2 bytes determine the size of the
261       * segment, excluding the header, but including the segment size
262       *
263       */
264      if($header==self::SEGMENT_HEAD)
265      {
266        $header=ConvertData::toUByte(fread($fileHandler, 1));
267        if(self::isValidMarkerHeader($header))
268        {
269          $length=ConvertData::toUShort(fread($fileHandler, 2), BYTE_ORDER_BIG_ENDIAN);
270          /*
271           * inside a segment, length include the 2 byte designed for the length
272           * size, so to read the associated data, needs to read length-2bytes
273           *
274           * the length exclude the 2bytes needed for the header identifier
275           *
276           */
277          $datas=fread($fileHandler, $length-2);
278
279          switch($header)
280          {
281            case self::SEGMENT_APP0:
282              if(preg_match('/^JFIF\x00/',$datas)>0)
283              {
284                $subType=self::APP0_JFIF;
285              }
286              elseif(preg_match('/^JFXX\x00\x10/',$datas)>0)
287              {
288                $subType=self::APP0_JFXX;
289              }
290              elseif(preg_match('/^(II|MM).{4}HEAPJPGM/s',$datas)>0)
291              {
292                $subType=self::APP0_CIFF;
293              }
294              elseif(preg_match('/^AVI1/s',$datas)>0)
295              {
296                $subType=self::APP0_AVI1;
297              }
298              else
299              {
300                $subType=self::UNKNOWN;
301              }
302              break;
303            case self::SEGMENT_APP1:
304              if(preg_match('/^Exif\x00\x00/',$datas)>0)
305              {
306                $subType=self::APP1_EXIF;
307                $datas = substr($datas, 6);
308              }
309              elseif(preg_match('/^http:\/\/ns.adobe.com\/xmp\/extension\/\x00/',$datas)>0)
310              {
311                $subType=self::APP1_EXTENDEDXMP;
312              }
313              elseif(preg_match('/^(http:\/\/|<exif:)/',$datas)>0)
314              {
315                $subType=self::APP1_XMP;
316              }
317              else
318              {
319                $subType=self::UNKNOWN;
320              }
321              break;
322            case self::SEGMENT_APP2:
323              if(preg_match('/^ICC_PROFILE\x00/',$datas)>0)
324              {
325                $subType=self::APP2_ICCPROFILE;
326              }
327              elseif(preg_match('/^FPXR\x00/',$datas)>0)
328              {
329                $subType=self::APP2_FPXR;
330              }
331              elseif(preg_match('/^MPF\x00/',$datas)>0)
332              {
333                $subType=self::APP2_MPF;
334              }
335              elseif(preg_match('/^\xff\xd8\xff\xdb/',$datas)>0)
336              {
337                $subType=self::APP2_SAMSUNGLP;
338              }
339              else
340              {
341                $subType=self::UNKNOWN;
342              }
343              break;
344            case self::SEGMENT_APP3:
345              if(preg_match('/^(Meta|META|Exif)\x00\x00/',$datas)>0)
346              {
347                $subType=self::APP3_META;
348              }
349              elseif(preg_match('/^Stim\x00/',$datas)>0)
350              {
351                $subType=self::APP3_STIM;
352              }
353              else
354              {
355                $subType=self::UNKNOWN;
356              }
357              break;
358            case self::SEGMENT_APP4:
359              if(preg_match('/^SCALADO\x00/',$datas)>0)
360              {
361                $subType=self::APP4_SCALADO;
362              }
363              else
364              {
365                $subType=self::UNKNOWN;
366              }
367              break;
368            case self::SEGMENT_APP5:
369              if(preg_match('/^RMETA\x00/',$datas)>0)
370              {
371                $subType=self::APP5_RMETA;
372              }
373              else
374              {
375                $subType=self::UNKNOWN;
376              }
377              break;
378            case self::SEGMENT_APP6:
379              if(preg_match('/^EPPIM\x00/',$datas)>0)
380              {
381                $subType=self::APP6_EPPIM;
382              }
383              elseif(preg_match('/^NTIF\x00/',$datas)>0)
384              {
385                $subType=self::APP6_NTIF;
386              }
387              else
388              {
389                $subType=self::UNKNOWN;
390              }
391              break;
392            case self::SEGMENT_APP7:
393              $subType=self::UNKNOWN;
394              break;
395            case self::SEGMENT_APP8:
396              if(preg_match('/^SPIFF\x00/',$datas)>0)
397              {
398                $subType=self::APP8_SPIFF;
399              }
400              else
401              {
402                $subType=self::UNKNOWN;
403              }
404              break;
405            case self::SEGMENT_APP9:
406              $subType=self::UNKNOWN;
407              break;
408            case self::SEGMENT_APP10:
409              if(preg_match('/^UNICODE\x00/',$datas)>0)
410              {
411                $subType=self::APP10_COMMENT;
412              }
413              else
414              {
415                $subType=self::UNKNOWN;
416              }
417              break;
418            case self::SEGMENT_APP11:
419              $subType=self::UNKNOWN;
420              break;
421            case self::SEGMENT_APP12:
422              if(preg_match('/^(\[picture info\]|Type=)/',$datas)>0)
423              {
424                $subType=self::APP12_PICTUREINFO;
425              }
426              elseif(preg_match('/^Ducky/',$datas)>0)
427              {
428                $subType=self::APP12_DUCKY;
429              }
430              else
431              {
432                $subType=self::UNKNOWN;
433              }
434              break;
435            case self::SEGMENT_APP13:
436              if(preg_match('/^(Photoshop 3.0\0|Adobe_Photoshop2.5)/',$datas)>0)
437              {
438                $subType=self::APP13_PHOTOSHOP;
439              }
440              elseif(preg_match('/^Adobe_CM/',$datas)>0)
441              {
442                $subType=self::APP13_ADOBECM;
443              }
444              else
445              {
446                $subType=self::UNKNOWN;
447              }
448              break;
449            case self::SEGMENT_APP14:
450              if(preg_match('/^Adobe/',$datas)>0)
451              {
452                $subType=self::APP14_ADOBE;
453              }
454              else
455              {
456                $subType=self::UNKNOWN;
457              }
458              break;
459            case self::SEGMENT_APP15:
460              if(preg_match('/^Q\s*(\d+)/',$datas)>0)
461              {
462                $subType=self::APP15_GRAPHICCONVERTER;
463              }
464              else
465              {
466                $subType=self::UNKNOWN;
467              }
468              break;
469            case self::SEGMENT_DQT:
470                $subType=self::DQT;
471              break;
472            case self::SEGMENT_SOF0:
473                $subType=self::SOF0;
474              break;
475            case self::SEGMENT_SOF1:
476                $subType=self::SOF1;
477              break;
478            case self::SEGMENT_SOF2:
479                $subType=self::SOF2;
480              break;
481            case self::SEGMENT_SOF3:
482                $subType=self::SOF3;
483              break;
484            case self::SEGMENT_SOF5:
485                $subType=self::SOF5;
486              break;
487            case self::SEGMENT_SOF6:
488                $subType=self::SOF6;
489              break;
490            case self::SEGMENT_SOF7:
491                $subType=self::SOF7;
492              break;
493            case self::SEGMENT_SOF9:
494                $subType=self::SOF9;
495              break;
496            case self::SEGMENT_SOFA:
497                $subType=self::SOFA;
498              break;
499            case self::SEGMENT_SOFB:
500                $subType=self::SOFB;
501              break;
502            case self::SEGMENT_SOFD:
503                $subType=self::SOFD;
504              break;
505            case self::SEGMENT_SOFE:
506                $subType=self::SOFE;
507              break;
508            case self::SEGMENT_SOFF:
509                $subType=self::SOFF;
510              break;
511            case self::SEGMENT_DHT:
512                $subType=self::DHT;
513              break;
514            case self::SEGMENT_JPG:
515                $subType=self::JPG;
516              break;
517            case self::SEGMENT_DAC:
518                $subType=self::DAC;
519              break;
520            case self::SEGMENT_SOS:
521                $subType=self::SOS;
522              break;
523            case self::SEGMENT_DRI:
524                $subType=self::DRI;
525              break;
526            case self::SEGMENT_COM:
527                $subType=self::COM;
528                break;
529            default:
530              /* in other case, return an new object Marker */
531              $subType=self::UNKNOWN;
532          }
533          return(new AppMarkerSegmentReader($header, $offset, $length+2, $subType, $datas));
534        }
535      }
536
537      return(false);
538    }
539
540    /**
541     * returns true if the given segment marker is a known segment
542     *
543     * @param UByte $value : the identifier of the segment
544     * @return Boolean
545     */
546    static function isValidMarkerHeader($value)
547    {
548      if($value==self::SEGMENT_HEAD or
549         $value==self::SEGMENT_APP0 or
550         $value==self::SEGMENT_APP1 or
551         $value==self::SEGMENT_APP2 or
552         $value==self::SEGMENT_APP3 or
553         $value==self::SEGMENT_APP4 or
554         $value==self::SEGMENT_APP5 or
555         $value==self::SEGMENT_APP6 or
556         $value==self::SEGMENT_APP7 or
557         $value==self::SEGMENT_APP8 or
558         $value==self::SEGMENT_APP9 or
559         $value==self::SEGMENT_APP10 or
560         $value==self::SEGMENT_APP11 or
561         $value==self::SEGMENT_APP12 or
562         $value==self::SEGMENT_APP13 or
563         $value==self::SEGMENT_APP14 or
564         $value==self::SEGMENT_APP15 or
565         $value==self::SEGMENT_SOF0 or
566         $value==self::SEGMENT_SOF1 or
567         $value==self::SEGMENT_SOF2 or
568         $value==self::SEGMENT_SOF3 or
569         $value==self::SEGMENT_SOF5 or
570         $value==self::SEGMENT_SOF6 or
571         $value==self::SEGMENT_SOF7 or
572         $value==self::SEGMENT_SOF9 or
573         $value==self::SEGMENT_SOFA or
574         $value==self::SEGMENT_SOFB or
575         $value==self::SEGMENT_SOFD or
576         $value==self::SEGMENT_SOFE or
577         $value==self::SEGMENT_SOFF or
578         $value==self::SEGMENT_DHT  or
579         $value==self::SEGMENT_JPG  or
580         $value==self::SEGMENT_DAC  or
581         $value==self::SEGMENT_SOS  or
582         $value==self::SEGMENT_DRI  or
583         $value==self::SEGMENT_COM  or
584         $value==self::SEGMENT_DQT)
585      {
586        return(true);
587      }
588      return(false);
589    }
590
591    /**
592     * returns the name of the given segment
593     *
594     * @param UByte $value : the identifier of the segment
595     *
596     * @return String
597     */
598    static function markerName($value)
599    {
600      switch($value)
601      {
602        case self::SEGMENT_APP0:
603          return("APP0");
604        case self::SEGMENT_APP1:
605          return("APP1");
606        case self::SEGMENT_APP2:
607          return("APP2");
608        case self::SEGMENT_APP3:
609          return("APP3");
610        case self::SEGMENT_APP4:
611          return("APP4");
612        case self::SEGMENT_APP5:
613          return("APP5");
614        case self::SEGMENT_APP6:
615          return("APP6");
616        case self::SEGMENT_APP7:
617          return("APP7");
618        case self::SEGMENT_APP8:
619          return("APP8");
620        case self::SEGMENT_APP9:
621          return("APP9");
622        case self::SEGMENT_APP10:
623          return("APP10");
624        case self::SEGMENT_APP11:
625          return("APP11");
626        case self::SEGMENT_APP12:
627          return("APP12");
628        case self::SEGMENT_APP13:
629          return("APP13");
630        case self::SEGMENT_APP14:
631          return("APP14");
632        case self::SEGMENT_APP15:
633          return("APP15");
634        case self::SEGMENT_DQT:
635          return("DQT");
636        case self::SEGMENT_SOF0:
637          return("SOF0");
638        case self::SEGMENT_SOF1:
639          return("SOF1");
640        case self::SEGMENT_SOF2:
641          return("SOF2");
642        case self::SEGMENT_SOF3:
643          return("SOF3");
644        case self::SEGMENT_SOF5:
645          return("SOF5");
646        case self::SEGMENT_SOF6:
647          return("SOF6");
648        case self::SEGMENT_SOF7:
649          return("SOF7");
650        case self::SEGMENT_SOF9:
651          return("SOF9");
652        case self::SEGMENT_SOFA:
653          return("SOFA");
654        case self::SEGMENT_SOFB:
655          return("SOFB");
656        case self::SEGMENT_SOFD:
657          return("SOFD");
658        case self::SEGMENT_SOFE:
659          return("SOFE");
660        case self::SEGMENT_SOFF:
661          return("SOFF");
662        case self::SEGMENT_DHT:
663          return("DHT");
664        case self::SEGMENT_JPG:
665          return("JPG");
666        case self::SEGMENT_DAC:
667          return("DAC");
668        case self::SEGMENT_SOS:
669          return("SOS");
670        case self::SEGMENT_DRI:
671          return("DRI");
672        case self::SEGMENT_COM:
673          return("COM");
674        default:
675          return(sprintf("%02x", $value));
676      }
677    }
678
679
680    /**
681     *
682     *
683     * @param UByte $header   : the identifier of the segment
684     *
685     * @param ULong $offset   : offset of the segment in the jpeg file
686     *
687     * @param UShort $length  : size of the segment, including the header and
688     *                          including the segment size (4bytes)
689     *
690     * @param String $subType : the subtype of the segment (for example an APP1
691     *                          segment can have EXIF or XMP datas)
692     *
693     * @param String $data    : the raw data of the segment, excluding the
694     *                          segment size and header
695     *
696     */
697    function __construct($header, $offset, $length, $subType, $data)
698    {
699      $this->header = $header;
700      $this->offset = $offset;
701      $this->length = $length;
702      $this->subType = $subType;
703      $this->workData = new Data($data);
704
705      $this->readData();
706
707      $this->workData->clear();
708    }
709
710    function __destruct()
711    {
712      unset($this->workData);
713      unset($this->data);
714      unset($this->header);
715      unset($this->offset);
716      unset($this->length);
717      unset($this->subType);
718      unset($this->dataLoaded);
719    }
720
721    /**
722     * returns the identifiant of the segment (see the consts SEGMENT_xxxxxx for
723     * to know the list)
724     *
725     * @return UByte
726     */
727    public function getHeader()
728    {
729      return($this->header);
730    }
731
732
733    /**
734     * returns the offset of the segment in the jpeg file
735     *
736     * @return ULong
737     */
738    public function getOffset()
739    {
740      return($this->offset);
741    }
742
743    /**
744     * returns the length of the segment, including the segment header and the
745     * segment size (4 bytes)
746     *
747     * @return UShort
748     */
749    public function getLength()
750    {
751      return($this->length);
752    }
753
754    /**
755     * returns the subtype of the segment
756     *
757     * @return String
758     */
759    public function getSubType()
760    {
761      return($this->subType);
762    }
763
764    /**
765     * returns true if the segment data has been loaded
766     *
767     * @return Boolean
768     */
769    public function dataLoaded()
770    {
771      return($this->dataLoaded);
772    }
773
774    /**
775     * returns the data of the segment.
776     *
777     * @return depends on the segment subtype
778     */
779    public function getData()
780    {
781      return($this->data);
782    }
783
784
785    /**
786     * read the segment datas
787     */
788    private function readData()
789    {
790      switch($this->header)
791      {
792        case self::SEGMENT_APP0:
793          /* not implemented */
794          break;
795        case self::SEGMENT_APP1:
796          $this->readSegmentAPP1();
797          break;
798        case self::SEGMENT_APP2:
799          /* not implemented */
800          break;
801        case self::SEGMENT_APP3:
802          /* not implemented */
803          break;
804        case self::SEGMENT_APP4:
805          /* not implemented */
806          break;
807        case self::SEGMENT_APP5:
808          /* not implemented */
809          break;
810        case self::SEGMENT_APP6:
811          /* not implemented */
812          break;
813        case self::SEGMENT_APP7:
814          /* not implemented */
815          break;
816        case self::SEGMENT_APP8:
817          /* not implemented */
818          break;
819        case self::SEGMENT_APP9:
820          /* not implemented */
821          break;
822        case self::SEGMENT_APP10:
823          /* not implemented */
824          break;
825        case self::SEGMENT_APP11:
826          /* not implemented */
827          break;
828        case self::SEGMENT_APP12:
829          /* not implemented */
830          break;
831        case self::SEGMENT_APP13:
832          $this->readSegmentAPP13();
833          break;
834        case self::SEGMENT_APP14:
835          /* not implemented */
836          break;
837        case self::SEGMENT_APP15:
838          /* not implemented */
839          break;
840        case self::SEGMENT_COM:
841          $this->readSegmentCOM();
842          break;
843        default:
844          break;
845      }
846    }
847
848    /**
849     *  read the content of an "APP1" segment
850     */
851    private function readSegmentAPP1()
852    {
853      switch($this->subType)
854      {
855        case self::APP1_EXIF:
856          $this->data = new TiffReader($this->workData, $this->offset+10);
857          $this->dataLoaded=true;
858          break;
859        case self::APP1_XMP:
860          $this->data = new XmpReader($this->workData->readASCII());
861          $this->dataLoaded=true;
862          break;
863        case self::APP1_EXTENDEDXMP:
864          /* When Xmp data size exceed 65458 bytes, the Xmp tree is splited.
865           * Each chunk is written into the JPEG file within a separate APP1
866           * marker segment.
867           *
868           * Each ExtendedXMP marker segment contains:
869           *  - A null-terminated signature string of "http://ns.adobe.com/xmp/extension/".
870           *  - A 128-bit GUID stored as a 32-byte ASCII hex string, capital A-F,
871           *    no null termination. The GUID is a 128-bit MD5 digest of the
872           *    full ExtendedXMP serialization
873           *  - The full length of the ExtendedXMP serialization as a 32-bit
874           *    unsigned integer
875           *  - The offset of this portion as a 32-bit unsigned integer.
876           *  - The portion of the ExtendedXMP
877           *
878           * Each chunck is returned as an array :
879           *  - 'uid'    => the 128-bit GUID
880           *  - 'offset' => the offset
881           *  - 'data'   => xmp data packet
882           *
883           * Xmp Tree is rebuilded after all extendedXmp chunks are loaded, by
884           * the class calling the AppMarkerSegmentReader class (the JpegReader
885           * class)
886           */
887
888          $this->workData->setByteOrder(BYTE_ORDER_BIG_ENDIAN);
889          $this->data=Array(
890            'uid'    => $this->workData->readASCII(32, 35),
891            'offset' => $this->workData->readULong(71),
892            'data'   => $this->workData->readASCII(-1, 75)
893          );
894
895          $this->dataLoaded=true;
896          break;
897        default:
898          break;
899      }
900    }
901
902    /**
903     *  read the content of an "APP13" segment
904     */
905    private function readSegmentAPP13()
906    {
907      switch($this->subType)
908      {
909        case self::APP13_PHOTOSHOP:
910          $this->data = new IptcReader($this->workData->readASCII());
911          $this->dataLoaded=true;
912          break;
913        case self::APP13_ADOBECM:
914          break;
915        default:
916          break;
917      }
918    }
919
920    /**
921     *  read the content of an "COM" segment
922     */
923    private function readSegmentCOM()
924    {
925      $this->data = new ComReader($this->workData->readASCII());
926      $this->dataLoaded=true;
927    }
928
929    public function toString()
930    {
931      $returned="Header: 0xff".sprintf("%02x", $this->header)." (".self::markerName($this->header).")".
932                " ; offset: 0x".sprintf("%08x", $this->offset).
933                " ; length: 0x".sprintf("%04x", $this->length).
934                " ; subType: ".$this->subType;
935      return($returned);
936    }
937
938
939  }
940
941
942 ?>
Note: See TracBrowser for help on using the repository browser.