Completed
Push — vendor/getid3 ( 69b815...49c253 )
by Pauli
04:26 queued 01:37
created

Image_XMP::read_XMP_array_from_text()   F

Complexity

Conditions 36
Paths 44

Size

Total Lines 167

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 36
nc 44
nop 1
dl 0
loc 167
rs 3.3333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/////////////////////////////////////////////////////////////////
4
/// getID3() by James Heinrich <[email protected]>               //
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
 *************************************************************************************************/
34
class 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++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
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)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
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)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
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)
0 ignored issues
show
Bug Best Practice introduced by
The expression $xmp_data of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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
);
775