source: spip-zone/_core_/branches/spip-3.1/plugins/medias/lib/getid3/module.tag.xmp.php @ 113163

Last change on this file since 113163 was 113163, checked in by spip.franck@…, 5 months ago

Mise à jour de la lib getid en version 1.9.16, nous étions en 1.9.12
https://github.com/JamesHeinrich/getID3/blob/master/changelog.txt

File size: 20.2 KB
Line 
1<?php
2
3/////////////////////////////////////////////////////////////////
4/// getID3() by James Heinrich <info@getid3.org>               //
5//  available at https://github.com/JamesHeinrich/getID3       //
6//            or https://www.getid3.org                        //
7//            or http://getid3.sourceforge.net                 //
8//  see readme.txt for more details                            //
9/////////////////////////////////////////////////////////////////
10//                                                             //
11// module.tag.xmp.php                                          //
12// module for analyzing XMP metadata (e.g. in JPEG files)      //
13// dependencies: NONE                                          //
14//                                                             //
15/////////////////////////////////////////////////////////////////
16//                                                             //
17// Module originally written [2009-Mar-26] by                  //
18//      Nigel Barnes <ngbarnesØhotmail*com>                    //
19// Bundled into getID3 with permission                         //
20//   called by getID3 in module.graphic.jpg.php                //
21//                                                            ///
22/////////////////////////////////////////////////////////////////
23
24/**************************************************************************************************
25 * SWISScenter Source                                                              Nigel Barnes
26 *
27 *      Provides functions for reading information from the 'APP1' Extensible Metadata
28 *      Platform (XMP) segment of JPEG format files.
29 *      This XMP segment is XML based and contains the Resource Description Framework (RDF)
30 *      data, which itself can contain the Dublin Core Metadata Initiative (DCMI) information.
31 *
32 *      This code uses segments from the JPEG Metadata Toolkit project by Evan Hunter.
33 *************************************************************************************************/
34class Image_XMP
35{
36        /**
37        * @var string
38        * The name of the image file that contains the XMP fields to extract and modify.
39        * @see Image_XMP()
40        */
41        public $_sFilename = null;
42
43        /**
44        * @var array
45        * The XMP fields that were extracted from the image or updated by this class.
46        * @see getAllTags()
47        */
48        public $_aXMP = array();
49
50        /**
51        * @var boolean
52        * True if an APP1 segment was found to contain XMP metadata.
53        * @see isValid()
54        */
55        public $_bXMPParse = false;
56
57        /**
58        * Returns the status of XMP parsing during instantiation
59        *
60        * You'll normally want to call this method before trying to get XMP fields.
61        *
62        * @return boolean
63        * Returns true if an APP1 segment was found to contain XMP metadata.
64        */
65        public function isValid()
66        {
67                return $this->_bXMPParse;
68        }
69
70        /**
71        * Get a copy of all XMP tags extracted from the image
72        *
73        * @return array - An array of XMP fields as it extracted by the XMPparse() function
74        */
75        public function getAllTags()
76        {
77                return $this->_aXMP;
78        }
79
80        /**
81        * Reads all the JPEG header segments from an JPEG image file into an array
82        *
83        * @param string $filename - the filename of the JPEG file to read
84        * @return array|boolean  $headerdata - Array of JPEG header segments,
85        *                        FALSE - if headers could not be read
86        */
87        public function _get_jpeg_header_data($filename)
88        {
89                // prevent refresh from aborting file operations and hosing file
90                ignore_user_abort(true);
91
92                // Attempt to open the jpeg file - the at symbol supresses the error message about
93                // not being able to open files. The file_exists would have been used, but it
94                // does not work with files fetched over http or ftp.
95                if (is_readable($filename) && is_file($filename) && ($filehnd = fopen($filename, 'rb'))) {
96                        // great
97                } else {
98                        return false;
99                }
100
101                // Read the first two characters
102                $data = fread($filehnd, 2);
103
104                // Check that the first two characters are 0xFF 0xD8  (SOI - Start of image)
105                if ($data != "\xFF\xD8")
106                {
107                        // No SOI (FF D8) at start of file - This probably isn't a JPEG file - close file and return;
108                        echo '<p>This probably is not a JPEG file</p>'."\n";
109                        fclose($filehnd);
110                        return false;
111                }
112
113                // Read the third character
114                $data = fread($filehnd, 2);
115
116                // Check that the third character is 0xFF (Start of first segment header)
117                if ($data{0} != "\xFF")
118                {
119                        // NO FF found - close file and return - JPEG is probably corrupted
120                        fclose($filehnd);
121                        return false;
122                }
123
124                // Flag that we havent yet hit the compressed image data
125                $hit_compressed_image_data = false;
126
127        $headerdata = array();
128                // Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
129                //                                       2) we have hit the compressed image data (no more headers are allowed after data)
130                //                                       3) or end of file is hit
131
132                while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
133                {
134                        // Found a segment to look at.
135                        // Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
136                        if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
137                        {
138                                // Segment isn't a Restart marker
139                                // Read the next two bytes (size)
140                                $sizestr = fread($filehnd, 2);
141
142                                // convert the size bytes to an integer
143                                $decodedsize = unpack('nsize', $sizestr);
144
145                                // Save the start position of the data
146                                $segdatastart = ftell($filehnd);
147
148                                // Read the segment data with length indicated by the previously read size
149                                $segdata = fread($filehnd, $decodedsize['size'] - 2);
150
151                                // Store the segment information in the output array
152                                $headerdata[] = array(
153                                        'SegType'      => ord($data{1}),
154                                        'SegName'      => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
155                                        'SegDataStart' => $segdatastart,
156                                        'SegData'      => $segdata,
157                                );
158                        }
159
160                        // If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
161                        if ($data{1} == "\xDA")
162                        {
163                                // Flag that we have hit the compressed image data - exit loop as no more headers available.
164                                $hit_compressed_image_data = true;
165                        }
166                        else
167                        {
168                                // Not an SOS - Read the next two bytes - should be the segment marker for the next segment
169                                $data = fread($filehnd, 2);
170
171                                // Check that the first byte of the two is 0xFF as it should be for a marker
172                                if ($data{0} != "\xFF")
173                                {
174                                        // NO FF found - close file and return - JPEG is probably corrupted
175                                        fclose($filehnd);
176                                        return false;
177                                }
178                        }
179                }
180
181                // Close File
182                fclose($filehnd);
183                // Alow the user to abort from now on
184                ignore_user_abort(false);
185
186                // Return the header data retrieved
187                return $headerdata;
188        }
189
190
191        /**
192        * Retrieves XMP information from an APP1 JPEG segment and returns the raw XML text as a string.
193        *
194        * @param string $filename - the filename of the JPEG file to read
195        * @return string|boolean $xmp_data - the string of raw XML text,
196        *                        FALSE - if an APP 1 XMP segment could not be found, or if an error occured
197        */
198        public function _get_XMP_text($filename)
199        {
200                //Get JPEG header data
201                $jpeg_header_data = $this->_get_jpeg_header_data($filename);
202
203                //Cycle through the header segments
204                for ($i = 0; $i < count($jpeg_header_data); $i++)
205                {
206                        // If we find an APP1 header,
207                        if (strcmp($jpeg_header_data[$i]['SegName'], 'APP1') == 0)
208                        {
209                                // And if it has the Adobe XMP/RDF label (http://ns.adobe.com/xap/1.0/\x00) ,
210                                if (strncmp($jpeg_header_data[$i]['SegData'], 'http://ns.adobe.com/xap/1.0/'."\x00", 29) == 0)
211                                {
212                                        // Found a XMP/RDF block
213                                        // Return the XMP text
214                                        $xmp_data = substr($jpeg_header_data[$i]['SegData'], 29);
215
216                                        return trim($xmp_data); // trim() should not be neccesary, but some files found in the wild with null-terminated block (known samples from Apple Aperture) causes problems elsewhere (see https://www.getid3.org/phpBB3/viewtopic.php?f=4&t=1153)
217                                }
218                        }
219                }
220                return false;
221        }
222
223        /**
224        * Parses a string containing XMP data (XML), and returns an array
225        * which contains all the XMP (XML) information.
226        *
227        * @param string $xmltext - a string containing the XMP data (XML) to be parsed
228        * @return array|boolean $xmp_array - an array containing all xmp details retrieved,
229        *                       FALSE - couldn't parse the XMP data.
230        */
231        public function read_XMP_array_from_text($xmltext)
232        {
233                // Check if there actually is any text to parse
234                if (trim($xmltext) == '')
235                {
236                        return false;
237                }
238
239                // Create an instance of a xml parser to parse the XML text
240                $xml_parser = xml_parser_create('UTF-8');
241
242                // 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
243
244                // We would like to remove unneccessary white space, but this will also
245                // remove things like newlines (&#xA;) in the XML values, so white space
246                // will have to be removed later
247                if (xml_parser_set_option($xml_parser, XML_OPTION_SKIP_WHITE, 0) == false)
248                {
249                        // Error setting case folding - destroy the parser and return
250                        xml_parser_free($xml_parser);
251                        return false;
252                }
253
254                // to use XML code correctly we have to turn case folding
255                // (uppercasing) off. XML is case sensitive and upper
256                // casing is in reality XML standards violation
257                if (xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0) == false)
258                {
259                        // Error setting case folding - destroy the parser and return
260                        xml_parser_free($xml_parser);
261                        return false;
262                }
263
264                // Parse the XML text into a array structure
265                if (xml_parse_into_struct($xml_parser, $xmltext, $values, $tags) == 0)
266                {
267                        // Error Parsing XML - destroy the parser and return
268                        xml_parser_free($xml_parser);
269                        return false;
270                }
271
272                // Destroy the xml parser
273                xml_parser_free($xml_parser);
274
275                // Clear the output array
276                $xmp_array = array();
277
278                // The XMP data has now been parsed into an array ...
279
280                // Cycle through each of the array elements
281                $current_property = ''; // current property being processed
282                $container_index = -1; // -1 = no container open, otherwise index of container content
283                foreach ($values as $xml_elem)
284                {
285                        // Syntax and Class names
286                        switch ($xml_elem['tag'])
287                        {
288                                case 'x:xmpmeta':
289                                        // only defined attribute is x:xmptk written by Adobe XMP Toolkit; value is the version of the toolkit
290                                        break;
291
292                                case 'rdf:RDF':
293                                        // required element immediately within x:xmpmeta; no data here
294                                        break;
295
296                                case 'rdf:Description':
297                                        switch ($xml_elem['type'])
298                                        {
299                                                case 'open':
300                                                case 'complete':
301                                                        if (array_key_exists('attributes', $xml_elem))
302                                                        {
303                                                                // rdf:Description may contain wanted attributes
304                                                                foreach (array_keys($xml_elem['attributes']) as $key)
305                                                                {
306                                                                        // Check whether we want this details from this attribute
307//                                                                      if (in_array($key, $GLOBALS['XMP_tag_captions']))
308                                                                        if (true)
309                                                                        {
310                                                                                // Attribute wanted
311                                                                                $xmp_array[$key] = $xml_elem['attributes'][$key];
312                                                                        }
313                                                                }
314                                                        }
315                                                        break;
316                                                case 'cdata':
317                                                case 'close':
318                                                        break;
319                                        }
320                                        break;
321
322                                case 'rdf:ID':
323                                case 'rdf:nodeID':
324                                        // Attributes are ignored
325                                        break;
326
327                                case 'rdf:li':
328                                        // Property member
329                                        if ($xml_elem['type'] == 'complete')
330                                        {
331                                                if (array_key_exists('attributes', $xml_elem))
332                                                {
333                                                        // If Lang Alt (language alternatives) then ensure we take the default language
334                                                        if (isset($xml_elem['attributes']['xml:lang']) && ($xml_elem['attributes']['xml:lang'] != 'x-default'))
335                                                        {
336                                                                break;
337                                                        }
338                                                }
339                                                if ($current_property != '')
340                                                {
341                                                        $xmp_array[$current_property][$container_index] = (isset($xml_elem['value']) ? $xml_elem['value'] : '');
342                                                        $container_index += 1;
343                                                }
344                                        //else unidentified attribute!!
345                                        }
346                                        break;
347
348                                case 'rdf:Seq':
349                                case 'rdf:Bag':
350                                case 'rdf:Alt':
351                                        // Container found
352                                        switch ($xml_elem['type'])
353                                        {
354                                                case 'open':
355                                                        $container_index = 0;
356                                                        break;
357                                                case 'close':
358                                                        $container_index = -1;
359                                                        break;
360                                                case 'cdata':
361                                                        break;
362                                        }
363                                        break;
364
365                                default:
366                                        // Check whether we want the details from this attribute
367//                                      if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
368                                        if (true)
369                                        {
370                                                switch ($xml_elem['type'])
371                                                {
372                                                        case 'open':
373                                                                // open current element
374                                                                $current_property = $xml_elem['tag'];
375                                                                break;
376
377                                                        case 'close':
378                                                                // close current element
379                                                                $current_property = '';
380                                                                break;
381
382                                                        case 'complete':
383                                                                // store attribute value
384                                                                $xmp_array[$xml_elem['tag']] = (isset($xml_elem['attributes']) ? $xml_elem['attributes'] : (isset($xml_elem['value']) ? $xml_elem['value'] : ''));
385                                                                break;
386
387                                                        case 'cdata':
388                                                                // ignore
389                                                                break;
390                                                }
391                                        }
392                                        break;
393                        }
394
395                }
396                return $xmp_array;
397        }
398
399
400        /**
401        * Constructor
402        *
403        * @param string $sFilename - Name of the image file to access and extract XMP information from.
404        */
405        public function __construct($sFilename)
406        {
407                $this->_sFilename = $sFilename;
408
409                if (is_file($this->_sFilename))
410                {
411                        // Get XMP data
412                        $xmp_data = $this->_get_XMP_text($sFilename);
413                        if ($xmp_data)
414                        {
415                                $aXMP = $this->read_XMP_array_from_text($xmp_data);
416                                if ($aXMP !== false) {
417                                        $this->_aXMP = (array) $aXMP;
418                                        $this->_bXMPParse = true;
419                                }
420                        }
421                }
422        }
423
424}
425
426/**
427* Global Variable: XMP_tag_captions
428*
429* The Property names of all known XMP fields.
430* Note: this is a full list with unrequired properties commented out.
431*/
432/*
433$GLOBALS['XMP_tag_captions'] = array(
434// IPTC Core
435        'Iptc4xmpCore:CiAdrCity',
436        'Iptc4xmpCore:CiAdrCtry',
437        'Iptc4xmpCore:CiAdrExtadr',
438        'Iptc4xmpCore:CiAdrPcode',
439        'Iptc4xmpCore:CiAdrRegion',
440        'Iptc4xmpCore:CiEmailWork',
441        'Iptc4xmpCore:CiTelWork',
442        'Iptc4xmpCore:CiUrlWork',
443        'Iptc4xmpCore:CountryCode',
444        'Iptc4xmpCore:CreatorContactInfo',
445        'Iptc4xmpCore:IntellectualGenre',
446        'Iptc4xmpCore:Location',
447        'Iptc4xmpCore:Scene',
448        'Iptc4xmpCore:SubjectCode',
449// Dublin Core Schema
450        'dc:contributor',
451        'dc:coverage',
452        'dc:creator',
453        'dc:date',
454        'dc:description',
455        'dc:format',
456        'dc:identifier',
457        'dc:language',
458        'dc:publisher',
459        'dc:relation',
460        'dc:rights',
461        'dc:source',
462        'dc:subject',
463        'dc:title',
464        'dc:type',
465// XMP Basic Schema
466        'xmp:Advisory',
467        'xmp:BaseURL',
468        'xmp:CreateDate',
469        'xmp:CreatorTool',
470        'xmp:Identifier',
471        'xmp:Label',
472        'xmp:MetadataDate',
473        'xmp:ModifyDate',
474        'xmp:Nickname',
475        'xmp:Rating',
476        'xmp:Thumbnails',
477        'xmpidq:Scheme',
478// XMP Rights Management Schema
479        'xmpRights:Certificate',
480        'xmpRights:Marked',
481        'xmpRights:Owner',
482        'xmpRights:UsageTerms',
483        'xmpRights:WebStatement',
484// These are not in spec but Photoshop CS seems to use them
485        'xap:Advisory',
486        'xap:BaseURL',
487        'xap:CreateDate',
488        'xap:CreatorTool',
489        'xap:Identifier',
490        'xap:MetadataDate',
491        'xap:ModifyDate',
492        'xap:Nickname',
493        'xap:Rating',
494        'xap:Thumbnails',
495        'xapidq:Scheme',
496        'xapRights:Certificate',
497        'xapRights:Copyright',
498        'xapRights:Marked',
499        'xapRights:Owner',
500        'xapRights:UsageTerms',
501        'xapRights:WebStatement',
502// XMP Media Management Schema
503        'xapMM:DerivedFrom',
504        'xapMM:DocumentID',
505        'xapMM:History',
506        'xapMM:InstanceID',
507        'xapMM:ManagedFrom',
508        'xapMM:Manager',
509        'xapMM:ManageTo',
510        'xapMM:ManageUI',
511        'xapMM:ManagerVariant',
512        'xapMM:RenditionClass',
513        'xapMM:RenditionParams',
514        'xapMM:VersionID',
515        'xapMM:Versions',
516        'xapMM:LastURL',
517        'xapMM:RenditionOf',
518        'xapMM:SaveID',
519// XMP Basic Job Ticket Schema
520        'xapBJ:JobRef',
521// XMP Paged-Text Schema
522        'xmpTPg:MaxPageSize',
523        'xmpTPg:NPages',
524        'xmpTPg:Fonts',
525        'xmpTPg:Colorants',
526        'xmpTPg:PlateNames',
527// Adobe PDF Schema
528        'pdf:Keywords',
529        'pdf:PDFVersion',
530        'pdf:Producer',
531// Photoshop Schema
532        'photoshop:AuthorsPosition',
533        'photoshop:CaptionWriter',
534        'photoshop:Category',
535        'photoshop:City',
536        'photoshop:Country',
537        'photoshop:Credit',
538        'photoshop:DateCreated',
539        'photoshop:Headline',
540        'photoshop:History',
541// Not in XMP spec
542        'photoshop:Instructions',
543        'photoshop:Source',
544        'photoshop:State',
545        'photoshop:SupplementalCategories',
546        'photoshop:TransmissionReference',
547        'photoshop:Urgency',
548// EXIF Schemas
549        'tiff:ImageWidth',
550        'tiff:ImageLength',
551        'tiff:BitsPerSample',
552        'tiff:Compression',
553        'tiff:PhotometricInterpretation',
554        'tiff:Orientation',
555        'tiff:SamplesPerPixel',
556        'tiff:PlanarConfiguration',
557        'tiff:YCbCrSubSampling',
558        'tiff:YCbCrPositioning',
559        'tiff:XResolution',
560        'tiff:YResolution',
561        'tiff:ResolutionUnit',
562        'tiff:TransferFunction',
563        'tiff:WhitePoint',
564        'tiff:PrimaryChromaticities',
565        'tiff:YCbCrCoefficients',
566        'tiff:ReferenceBlackWhite',
567        'tiff:DateTime',
568        'tiff:ImageDescription',
569        'tiff:Make',
570        'tiff:Model',
571        'tiff:Software',
572        'tiff:Artist',
573        'tiff:Copyright',
574        'exif:ExifVersion',
575        'exif:FlashpixVersion',
576        'exif:ColorSpace',
577        'exif:ComponentsConfiguration',
578        'exif:CompressedBitsPerPixel',
579        'exif:PixelXDimension',
580        'exif:PixelYDimension',
581        'exif:MakerNote',
582        'exif:UserComment',
583        'exif:RelatedSoundFile',
584        'exif:DateTimeOriginal',
585        'exif:DateTimeDigitized',
586        'exif:ExposureTime',
587        'exif:FNumber',
588        'exif:ExposureProgram',
589        'exif:SpectralSensitivity',
590        'exif:ISOSpeedRatings',
591        'exif:OECF',
592        'exif:ShutterSpeedValue',
593        'exif:ApertureValue',
594        'exif:BrightnessValue',
595        'exif:ExposureBiasValue',
596        'exif:MaxApertureValue',
597        'exif:SubjectDistance',
598        'exif:MeteringMode',
599        'exif:LightSource',
600        'exif:Flash',
601        'exif:FocalLength',
602        'exif:SubjectArea',
603        'exif:FlashEnergy',
604        'exif:SpatialFrequencyResponse',
605        'exif:FocalPlaneXResolution',
606        'exif:FocalPlaneYResolution',
607        'exif:FocalPlaneResolutionUnit',
608        'exif:SubjectLocation',
609        'exif:SensingMethod',
610        'exif:FileSource',
611        'exif:SceneType',
612        'exif:CFAPattern',
613        'exif:CustomRendered',
614        'exif:ExposureMode',
615        'exif:WhiteBalance',
616        'exif:DigitalZoomRatio',
617        'exif:FocalLengthIn35mmFilm',
618        'exif:SceneCaptureType',
619        'exif:GainControl',
620        'exif:Contrast',
621        'exif:Saturation',
622        'exif:Sharpness',
623        'exif:DeviceSettingDescription',
624        'exif:SubjectDistanceRange',
625        'exif:ImageUniqueID',
626        'exif:GPSVersionID',
627        'exif:GPSLatitude',
628        'exif:GPSLongitude',
629        'exif:GPSAltitudeRef',
630        'exif:GPSAltitude',
631        'exif:GPSTimeStamp',
632        'exif:GPSSatellites',
633        'exif:GPSStatus',
634        'exif:GPSMeasureMode',
635        'exif:GPSDOP',
636        'exif:GPSSpeedRef',
637        'exif:GPSSpeed',
638        'exif:GPSTrackRef',
639        'exif:GPSTrack',
640        'exif:GPSImgDirectionRef',
641        'exif:GPSImgDirection',
642        'exif:GPSMapDatum',
643        'exif:GPSDestLatitude',
644        'exif:GPSDestLongitude',
645        'exif:GPSDestBearingRef',
646        'exif:GPSDestBearing',
647        'exif:GPSDestDistanceRef',
648        'exif:GPSDestDistance',
649        'exif:GPSProcessingMethod',
650        'exif:GPSAreaInformation',
651        'exif:GPSDifferential',
652        'stDim:w',
653        'stDim:h',
654        'stDim:unit',
655        'xapGImg:height',
656        'xapGImg:width',
657        'xapGImg:format',
658        'xapGImg:image',
659        'stEvt:action',
660        'stEvt:instanceID',
661        'stEvt:parameters',
662        'stEvt:softwareAgent',
663        'stEvt:when',
664        'stRef:instanceID',
665        'stRef:documentID',
666        'stRef:versionID',
667        'stRef:renditionClass',
668        'stRef:renditionParams',
669        'stRef:manager',
670        'stRef:managerVariant',
671        'stRef:manageTo',
672        'stRef:manageUI',
673        'stVer:comments',
674        'stVer:event',
675        'stVer:modifyDate',
676        'stVer:modifier',
677        'stVer:version',
678        'stJob:name',
679        'stJob:id',
680        'stJob:url',
681// Exif Flash
682        'exif:Fired',
683        'exif:Return',
684        'exif:Mode',
685        'exif:Function',
686        'exif:RedEyeMode',
687// Exif OECF/SFR
688        'exif:Columns',
689        'exif:Rows',
690        'exif:Names',
691        'exif:Values',
692// Exif CFAPattern
693        'exif:Columns',
694        'exif:Rows',
695        'exif:Values',
696// Exif DeviceSettings
697        'exif:Columns',
698        'exif:Rows',
699        'exif:Settings',
700);
701*/
702
703/**
704* Global Variable: JPEG_Segment_Names
705*
706* The names of the JPEG segment markers, indexed by their marker number
707*/
708$GLOBALS['JPEG_Segment_Names'] = array(
709        0x01 => 'TEM',
710        0x02 => 'RES',
711        0xC0 => 'SOF0',
712        0xC1 => 'SOF1',
713        0xC2 => 'SOF2',
714        0xC3 => 'SOF4',
715        0xC4 => 'DHT',
716        0xC5 => 'SOF5',
717        0xC6 => 'SOF6',
718        0xC7 => 'SOF7',
719        0xC8 => 'JPG',
720        0xC9 => 'SOF9',
721        0xCA => 'SOF10',
722        0xCB => 'SOF11',
723        0xCC => 'DAC',
724        0xCD => 'SOF13',
725        0xCE => 'SOF14',
726        0xCF => 'SOF15',
727        0xD0 => 'RST0',
728        0xD1 => 'RST1',
729        0xD2 => 'RST2',
730        0xD3 => 'RST3',
731        0xD4 => 'RST4',
732        0xD5 => 'RST5',
733        0xD6 => 'RST6',
734        0xD7 => 'RST7',
735        0xD8 => 'SOI',
736        0xD9 => 'EOI',
737        0xDA => 'SOS',
738        0xDB => 'DQT',
739        0xDC => 'DNL',
740        0xDD => 'DRI',
741        0xDE => 'DHP',
742        0xDF => 'EXP',
743        0xE0 => 'APP0',
744        0xE1 => 'APP1',
745        0xE2 => 'APP2',
746        0xE3 => 'APP3',
747        0xE4 => 'APP4',
748        0xE5 => 'APP5',
749        0xE6 => 'APP6',
750        0xE7 => 'APP7',
751        0xE8 => 'APP8',
752        0xE9 => 'APP9',
753        0xEA => 'APP10',
754        0xEB => 'APP11',
755        0xEC => 'APP12',
756        0xED => 'APP13',
757        0xEE => 'APP14',
758        0xEF => 'APP15',
759        0xF0 => 'JPG0',
760        0xF1 => 'JPG1',
761        0xF2 => 'JPG2',
762        0xF3 => 'JPG3',
763        0xF4 => 'JPG4',
764        0xF5 => 'JPG5',
765        0xF6 => 'JPG6',
766        0xF7 => 'JPG7',
767        0xF8 => 'JPG8',
768        0xF9 => 'JPG9',
769        0xFA => 'JPG10',
770        0xFB => 'JPG11',
771        0xFC => 'JPG12',
772        0xFD => 'JPG13',
773        0xFE => 'COM',
774);
Note: See TracBrowser for help on using the repository browser.