phpExifRW
[ class tree: phpExifRW ] [ index: phpExifRW ] [ all elements ]

Source for file exif.inc

Documentation is available at exif.inc


1 <?php
2 /**
3 * PHP Class to read, write and transfer EXIF information
4 * that most of the digital camera produces.
5 *
6 * This class is based on jhead (in C) by Matthias Wandel
7 *
8 * Vinay Yadav (vinayRas) < vinay@sanisoft.com >
9 * http://www.sanisoft.com/phpexifrw/
10 *
11 * For more information on EXIF
12 * http://www.pima.net/standards/it10/PIMA15740/exif.htm
13 *
14 * Features:
15 * - Read Exif Information
16 * - Extract and display emdedded thumbnails
17 * - Transfer Exif Information
18 * - Add Comments to files.
19 * - Tranfering EXIF information from one file to another.
20 *
21 * TODO
22 * 1. Writing exif information to the file.
23 * 2. Add EXIF audio reading methods (I think it exists!)
24 * 3. Support of additional tags.
25 * 4. Handling Unicode character in UserComment tag of EXif Information.
26 */
27
28 /** * Start Of Frame N */
28
29 define("M_SOF0",0xC0);
30 /** * N indicates which compression process */
30
31 define("M_SOF1",0xC1);
32 /** * Only SOF0-SOF2 are now in common use */
32
33 define("M_SOF2",0xC2);
34 /** * */
34
35 define("M_SOF3",0xC3);
36 /** * NB: codes C4 and CC are NOT SOF markers */
36
37 define("M_SOF5",0xC5);
38 /** * */
38
39 define("M_SOF6",0xC6);
40 /** * */
40
41 define("M_SOF7",0xC7);
42 /** * */
42
43 define("M_SOF9",0xC9);
44 /** * */
44
45 define("M_SOF10",0xCA);
46 /** * */
46
47 define("M_SOF11",0xCB);
48 /** * */
48
49 define("M_SOF13",0xCD);
50 /** * */
50
51 define("M_SOF14",0xCE);
52 /** * */
52
53 define("M_SOF15",0xCF);
54 /** * Start Of Image (beginning of datastream) */
54
55 define("M_SOI",0xD8);
56 /** * End Of Image (end of datastream) */
56
57 define("M_EOI",0xD9);
58 /** * Start Of Scan (begins compressed data) */
58
59 define("M_SOS",0xDA);
60 /** * Jfif marker */
60
61 define("M_JFIF",0xE0);
62 /** * Exif marker */
62
63 define("M_EXIF",0xE1);
64 /** * Image Title */
64
65 define("M_COM",0xFE);
66
67 define("NUM_FORMATS","12");
68
69 /** * Tag Data Format */
69
70 define("FMT_BYTE","1");
71 /** * ASCII */
71
72 define("FMT_STRING","2");
73 /** * Short */
73
74 define("FMT_USHORT","3");
75 /** * Long */
75
76 define("FMT_ULONG","4");
77 /** * Rational */
77
78 define("FMT_URATIONAL","5");
79 /** * Byte */
79
80 define("FMT_SBYTE","6");
81 /** * Undefined */
81
82 define("FMT_UNDEFINED","7");
83 /** * Short */
83
84 define("FMT_SSHORT","8");
85 /** * Long */
85
86 define("FMT_SLONG","9");
87 /** * Rational */
87
88 define("FMT_SRATIONAL","10");
89 /** * Single */
89
90 define("FMT_SINGLE","11");
91 /** * Double */
91
92 define("FMT_DOUBLE","12");
93
94 /** * Exif IFD */
94
95 define("TAG_EXIF_OFFSET","0x8769");
96 /** * Interoperability tag */
96
97 define("TAG_INTEROP_OFFSET","0xa005");
98 /** * Image input equipment manufacturer */
98
99 define("TAG_MAKE","0x010F");
100 /** * Image input equipment model */
100
101 define("TAG_MODEL","0x0110");
102 /** * Orientation of image */
102
103 define("TAG_ORIENTATION","0x0112");
104 /** * Exposure Time */
104
105 define("TAG_EXPOSURETIME","0x829A");
106 /** * F Number */
106
107 define("TAG_FNUMBER","0x829D");
108 /** * Shutter Speed */
108
109 define("TAG_SHUTTERSPEED","0x9201");
110 /** * Aperture */
110
111 define("TAG_APERTURE","0x9202");
112 /** * Aperture */
112
113 define("TAG_MAXAPERTURE","0x9205");
114 /** * Lens Focal Length */
114
115 define("TAG_FOCALLENGTH","0x920A");
116 /** * The date and time when the original image data was generated. */
116
117 define("TAG_DATETIME_ORIGINAL","0x9003");
118 /** * User Comments */
118
119 define("TAG_USERCOMMENT","0x9286");
120 /** * subject Location */
120
121 define("TAG_SUBJECT_DISTANCE","0x9206");
122 /** * Flash */
122
123 define("TAG_FLASH","0x9209");
124 /** * Focal Plane X Resolution */
124
125 define("TAG_FOCALPLANEXRES","0xa20E");
126 /** * Focal Plane Resolution Units */
126
127 define("TAG_FOCALPLANEUNITS","0xa210");
128 /** * Image Width */
128
129 define("TAG_EXIF_IMAGEWIDTH","0xA002");
130 /** * Image Height */
130
131 define("TAG_EXIF_IMAGELENGTH","0xA003");
132 /** * Exposure Bias */
132
133 define("TAG_EXPOSURE_BIAS","0x9204");
134 /** * Light Source */
134
135 define("TAG_WHITEBALANCE","0x9208");
136 /** * Metering Mode */
136
137 define("TAG_METERING_MODE","0x9207");
138 /** * Exposure Program */
138
139 define("TAG_EXPOSURE_PROGRAM","0x8822");
140 /** * ISO Equivalent Speed Rating */
140
141 define("TAG_ISO_EQUIVALENT","0x8827");
142 /** * Compressed Bits Per Pixel */
142
143 define("TAG_COMPRESSION_LEVEL","0x9102");
144 /** * Thumbnail Start Offset */
144
145 define("TAG_THUMBNAIL_OFFSET","0x0201");
146 /** * Thumbnail Length */
146
147 define("TAG_THUMBNAIL_LENGTH","0x0202");
148 /** * Image Marker */
148
149 define("PSEUDO_IMAGE_MARKER",0x123);
150 /** * Max Image Title Length */
150
151 define("MAX_COMMENT",2000);
152
153 /**
154 * As more and more tags will be added will, the contents of array will increase.
155 * DONT remove any blank array, since they do contain several tags.
156 */
157
158 $FMT_BYTE_ARRAY = array();
159 $FMT_STRING_ARRAY = array(
160 0x010E, //Image title
161 0x010F, // Make - Image input equipment manufacturer
162 0x0110, // Model - Image input equipment model
163 0x0131, // Software - Software used
164 0x013B, // Artist - Person who created the image
165 0x8298,// Copyright - Copyright holder
166 0x9003, // DateTimeOriginal - Date and time of original data generation
167 );
168 $FMT_USHORT_ARRAY = array(
169 0x0112, // Orientation
170 0x8822, // Exposure Program
171 0x9207, // Metering mode
172 0x9209, // Flash
173 0xA002, // Valid image width PixelXDimension
174 0xA003, // Valid image height PixelYDimension
175 );
176 $FMT_ULONG_ARRAY = array(
177 0x0202, // JPEGInterchangeFormatLength
178 );
179 $FMT_URATIONAL_ARRAY = array(
180 0x829A, // Exposure Time
181 0x829D, // F Number
182 0x9102, // CompressedBitsPerPixel
183 0x9202, // Aperture
184 0x9205, // MaxApertureValue
185 0x920A, // focal length
186 );
187 $FMT_SBYTE_ARRAY = array();
188 $FMT_UNDEFINED_ARRAY = array();
189 $FMT_SSHORT_ARRAY = array();
190 $FMT_SLONG_ARRAY = array();
191 $FMT_SRATIONAL_ARRAY = array(
192 0x9201, // shutter speed
193 0x9204, // Exposure Bias
194 );
195 $FMT_SINGLE_ARRAY = array();
196 $FMT_DOUBLE_ARRAY = array();
197
198 /** error Description */
198
199 /**
200 1 - File does not exists!
201 2 -
202 3 - Filename not provided
203
204 10 - too many padding bytes
205 11 - "invalid marker"
206 12 - Premature end of file?
207
208
209 51 - "Illegal subdirectory link"
210 52 - "NOT EXIF FORMAT"
211 53 - "Invalid Exif alignment marker.\n"
212 54 - "Invalid Exif start (1)"
213
214 */
215
216 $TagTable = array(
217 array( 0x100, "ImageWidth"),
218 array( 0x101, "ImageLength"),
219 array( 0x102, "BitsPerSample"),
220 array( 0x103, "Compression"),
221 array( 0x106, "PhotometricInterpretation"),
222 array( 0x10A, "FillOrder"),
223 array( 0x10D, "DocumentName"),
224 array( 0x10E, "ImageDescription"),
225 array( 0x10F, "Make"),
226 array( 0x110, "Model"),
227 array( 0x111, "StripOffsets"),
228 array( 0x112, "Orientation"),
229 array( 0x115, "SamplesPerPixel"),
230 array( 0x116, "RowsPerStrip"),
231 array( 0x117, "StripByteCounts"),
232 array( 0x11A, "XResolution"),
233 array( 0x11B, "YResolution"),
234 array( 0x11C, "PlanarConfiguration"),
235 array( 0x128, "ResolutionUnit"),
236 array( 0x12D, "TransferFunction"),
237 array( 0x131, "Software"),
238 array( 0x132, "DateTime"),
239 array( 0x13B, "Artist"),
240 array( 0x13E, "WhitePoint"),
241 array( 0x13F, "PrimaryChromaticities"),
242 array( 0x156, "TransferRange"),
243 array( 0x200, "JPEGProc"),
244 array( 0x201, "ThumbnailOffset"),
245 array( 0x202, "ThumbnailLength"),
246 array( 0x211, "YCbCrCoefficients"),
247 array( 0x212, "YCbCrSubSampling"),
248 array( 0x213, "YCbCrPositioning"),
249 array( 0x214, "ReferenceBlackWhite"),
250 array( 0x828D, "CFARepeatPatternDim"),
251 array( 0x828E, "CFAPattern"),
252 array( 0x828F, "BatteryLevel"),
253 array( 0x8298, "Copyright"),
254 array( 0x829A, "ExposureTime"),
255 array( 0x829D, "FNumber"),
256 array( 0x83BB, "IPTC/NAA"),
257 array( 0x8769, "ExifOffset"),
258 array( 0x8773, "InterColorProfile"),
259 array( 0x8822, "ExposureProgram"),
260 array( 0x8824, "SpectralSensitivity"),
261 array( 0x8825, "GPSInfo"),
262 array( 0x8827, "ISOSpeedRatings"),
263 array( 0x8828, "OECF"),
264 array( 0x9000, "ExifVersion"),
265 array( 0x9003, "DateTimeOriginal"),
266 array( 0x9004, "DateTimeDigitized"),
267 array( 0x9101, "ComponentsConfiguration"),
268 array( 0x9102, "CompressedBitsPerPixel"),
269 array( 0x9201, "ShutterSpeedValue"),
270 array( 0x9202, "ApertureValue"),
271 array( 0x9203, "BrightnessValue"),
272 array( 0x9204, "ExposureBiasValue"),
273 array( 0x9205, "MaxApertureValue"),
274 array( 0x9206, "SubjectDistance"),
275 array( 0x9207, "MeteringMode"),
276 array( 0x9208, "LightSource"),
277 array( 0x9209, "Flash"),
278 array( 0x920A, "FocalLength"),
279 array( 0x927C, "MakerNote"),
280 array( 0x9286, "UserComment"),
281 array( 0x9290, "SubSecTime"),
282 array( 0x9291, "SubSecTimeOriginal"),
283 array( 0x9292, "SubSecTimeDigitized"),
284 array( 0xA000, "FlashPixVersion"),
285 array( 0xA001, "ColorSpace"),
286 array( 0xA002, "ExifImageWidth"),
287 array( 0xA003, "ExifImageLength"),
288 array( 0xA005, "InteroperabilityOffset"),
289 array( 0xA20B, "FlashEnergy"), // 0x920B in TIFF/EP
290 array( 0xA20C, "SpatialFrequencyResponse"), // 0x920C - -
291 array( 0xA20E, "FocalPlaneXResolution"), // 0x920E - -
292 array( 0xA20F, "FocalPlaneYResolution"), // 0x920F - -
293 array( 0xA210, "FocalPlaneResolutionUnit"), // 0x9210 - -
294 array( 0xA214, "SubjectLocation"), // 0x9214 - -
295 array( 0xA215, "ExposureIndex"), // 0x9215 - -
296 array( 0xA217, "SensingMethod"), // 0x9217 - -
297 array( 0xA300, "FileSource"),
298 array( 0xA301, "SceneType"),
299 array( 0, NULL)
300 ) ;
301
302 $ProcessTable = array(
303 array(M_SOF0, "Baseline"),
304 array(M_SOF1, "Extended sequential"),
305 array(M_SOF2, "Progressive"),
306 array(M_SOF3, "Lossless"),
307 array(M_SOF5, "Differential sequential"),
308 array(M_SOF6, "Differential progressive"),
309 array(M_SOF7, "Differential lossless"),
310 array(M_SOF9, "Extended sequential, arithmetic coding"),
311 array(M_SOF10, "Progressive, arithmetic coding"),
312 array(M_SOF11, "Lossless, arithmetic coding"),
313 array(M_SOF13, "Differential sequential, arithmetic coding"),
314 array(M_SOF14, "Differential progressive, arithmetic coding"),
315 array(M_SOF15, "Differential lossless, arithmetic coding"),
316 array(0, "Unknown")
317 );
318
319 /**
320 * PHP Class to read, write and transfer EXIF information
321 * that most of the digital camera produces
322 * Currenty it can only read JPEG file.
323 */
324 /**
325 * @author Vinay Yadav (vinayRas) < vinay@sanisoft.com >
326 *
327 * @todo Writing exif information to the file.
328 * @todo Add EXIF audio reading methods (I think it exists!)
329 * @todo Support of additional tags.
330 * @todo Handling Unicode character in UserComment tag of EXif Information.
331 *
332 * @version 0.5
333 * @licence http://opensource.org/licenses/lgpl-license.php GNU LGPL
334 */
335 class phpExifRW {
336
337 /*
338 * Array containg all Exif and JPEG image attributes
339 * into regular expressions for themselves.
340 * $ImageInfo[TAG] = TAG_VALUE;
341 *
342 * @var array
343 * @access private
344 *
345 */
346 var $ImageInfo = array();
347
348 var $MotorolaOrder = 0;
349 var $ExifImageWidth = 0; //
350 var $FocalplaneXRes = 0; //
351 var $FocalplaneUnits = 0; //
352 var $sections = array();
353 var $currSection = 0; /** Stores total number fo Sections */
353
354
355 var $BytesPerFormat = array(0,1,1,2,4,8,1,1,2,4,8,4,8);
356
357 var $DirWithThumbnailPtrs = 0;
358 var $ThumbnailSize = 0;
359
360 var $ReadMode = array(
361 "READ_EXIF" => 1,
362 "READ_IMAGE" => 2,
363 "READ_ALL" => 3
364 );
365
366 var $ImageReadMode = 3; /** related to $RealMode arrays values */
366
367 var $file = ""; /** JPEG file to parse for EXIF data */
367
368 var $newFile = 1; /** flag to check if the current file has been parsed or not. */
368
369
370 var $thumbnail = ""; /* Name of thumbnail */
371 var $thumbnailURL = ""; /* */
372
373 var $exifSection = -1; // market the exif section index oout of all sections
374
375 var $errno = 0;
376 var $errstr = "";
377
378 var $debug = false;
379 var $showTags = false;
380
381 // Caching ralated variables
382 var $caching = true; /* Should cacheing of image thumnails be allowed? */
383 var $cacheDir = ""; /* Checkout constructor for default path. */
384
385 /**
386 * Constructor
387 * @param string File name to be parsed.
388 *
389 */
390 function phpExifRW($file = "") {
391
392 if(!empty($file)) {
393 $this->file = $file;
394 }
395
396 if($this->caching) {
397 $this->cacheDir = dirname(__FILE__)."/.cache_thumbs";
398
399 /**
400 * If Cache directory does not exists then attempt to create it.
401 */
402 if(!is_dir($this->cacheDir)) {
403 @mkdir($this->cacheDir);
404 }
405
406 // Prepare the ame of thumbnail
407 if(is_dir($this->cacheDir)) {
408 $this->thumbnail = $this->cacheDir."/".$this->file;
409 $this->thumbnailURL = ".cache_thumbs/$this->file";
410 }
411 }
412
413 /** check if file exists! */
414 if(!file_exists($this->file)) {
415 $this->errno = 1;
416 $this->errstr = "File '".$this->file."' does not exists!";
417 }
418 $this->currSection = 0;
419 }
420
421 /**
422 * Show Debugging information
423 *
424 * @param string Debugging message to display
425 * @param int Type of error (0 - Warning, 1 - Error)
426 * @return void
427 *
428 */
429 function debug($str,$TYPE = 0) {
430 if($this->debug) {
431 echo "<br>$str";
432 if($TYPE == 1) {
433 exit;
434 }
435 }
436 }
437
438 /**
439 * Processes the whole file.
440 *
441 */
442 function processFile() {
443 /** dont reparse the whole file. */
444 if(!$this->newFile) return true;
445
446 /** Open the JPEG in binary safe reading mode */
447 $fp = fopen($this->file,"rb");
448
449 $this->ImageInfo["FileName"] = $this->file;
450 $this->ImageInfo["FileSize"] = filesize($this->file); /** Size of the File */
451 $this->ImageInfo["FileDateTime"] = filectime($this->file); /** File node change time */
452
453 /** check whether jped image or not */
454 $a = fgetc($fp);
455 if (ord($a) != 0xff || ord(fgetc($fp)) != M_SOI){
456 $this->debug("Not a JPEG FILE",1);
457 $this->errorno = 1;
458 $this->errorstr = "File '".$this->file."' does not exists!";
459 }
460 $tmpTestLevel = 0;
461 /** Examines each byte one-by-one */
462 while(!feof($fp)) {
463 $data = array();
464 for ($a=0;$a<7;$a++){
465 $marker = fgetc($fp);
466 if (ord($marker) != 0xff) break;
467 if ($a >= 6){
468 $this->errno = 10;
469 $this->errstr = "too many padding bytes!";
470 $this->debug($this->errstr,1);
471 return false;
472 }
473 }
474
475 if (ord($marker) == 0xff){
476 // 0xff is legal padding, but if we get that many, something's wrong.
477 $this->errno = 10;
478 $this->errstr = "too many padding bytes!";
479 $this->debug($this->errstr,1);
480 }
481
482 $marker = ord($marker);
483 $this->sections[$this->currSection]["type"] = $marker;
484
485 // Read the length of the section.
486 $lh = ord(fgetc($fp));
487 $ll = ord(fgetc($fp));
488
489 $itemlen = ($lh << 8) | $ll;
490
491 if ($itemlen < 2){
492 $this->errno = 11;
493 $this->errstr = "invalid marker";
494 $this->debug($this->errstr,1);
495 }
496 $this->sections[$this->currSection]["size"] = $itemlen;
497
498 $tmpDataArr = array(); /** Temporary Array */
499
500 $tmpStr = fread($fp,$itemlen-2);
501
502 $tmpDataArr[] = chr($lh);
503 $tmpDataArr[] = chr($ll);
504
505 $chars = preg_split('//', $tmpStr, -1, PREG_SPLIT_NO_EMPTY);
506 $tmpDataArr = array_merge($tmpDataArr,$chars);
507
508 $data = $tmpDataArr;
509 $this->sections[$this->currSection]["data"] = $data;
510
511 if(count($data) != $itemlen) {
512 $this->errno = 12;
513 $this->errstr = "Premature end of file?";
514 $this->debug($this->errstr,1);
515 }
516
517 $this->currSection++; /** */
518
519 switch($marker) {
520 case M_SOS:
521 $this->debug("<br>Found '".M_SOS."' Section, Prcessing it... <br>");;
522 // If reading entire image is requested, read the rest of the data.
523 if ($this->ImageReadMode & $this->ReadMode["READ_IMAGE"]){
524 // Determine how much file is left.
525 $cp = ftell($fp);
526 fseek($fp,0, SEEK_END);
527 $ep = ftell($fp);
528 fseek($fp, $cp, SEEK_SET);
529
530 $size = $ep-$cp;
531 $got = fread($fp, $size);
532
533 $this->sections[$this->currSection]["data"] = $got;
534 $this->sections[$this->currSection]["size"] = $size;
535 $this->sections[$this->currSection]["type"] = $PSEUDO_IMAGE_MARKER;
536 $this->currSection++;
537 $HaveAll = 1;
538 $exitAll =1;
539 }
540 $this->debug("<br>'".M_SOS."' Section, PROCESSED<br>");
541 break;
542 case M_COM: // Comment section
543 $this->debug("<br>Found '".M_COM."'(Comment) Section, Processing<br>");
544 $this->process_COM($data, $itemlen);
545 $this->debug("<br>'".M_COM."'(Comment) Section, PROCESSED<br>");
546
547 $tmpTestLevel++;
548 break;
549 case M_SOI:
550 $this->debug(" <br> === START OF IMAGE =====<br>");
551 break;
552 case M_EOI:
553 $this->debug(" <br>=== END OF IMAGE =====<br> ");
554 break;
555 case M_JFIF:
556 // Regular jpegs always have this tag, exif images have the exif
557 // marker instead, althogh ACDsee will write images with both markers.
558 // this program will re-create this marker on absence of exif marker.
559 // hence no need to keep the copy from the file.
560 //echo " <br> === M_JFIF =====<br>";
561 $this->sections[--$this->currSection]["data"] = "";
562 break;
563 case M_EXIF:
564 // Seen files from some 'U-lead' software with Vivitar scanner
565 // that uses marker 31 for non exif stuff. Thus make sure
566 // it says 'Exif' in the section before treating it as exif.
567 $this->debug("<br>Found '".M_EXIF."'(Exif) Section, Proccessing<br>");
568 $this->exifSection = $this->currSection-1;
569 if (($this->ImageReadMode & $this->ReadMode["READ_EXIF"]) && ($data[2].$data[3].$data[4].$data[5]) == "Exif"){
570 $this->process_EXIF($data, $itemlen);
571 }else{
572 // Discard this section.
573 $this->sections[--$this->currSection]["data"] = "";
574 }
575 $this->debug("<br>'".M_EXIF."'(Exif) Section, PROCESSED<br>");
576 $tmpTestLevel++;
577 break;
578 case M_SOF0:
579 case M_SOF1:
580 case M_SOF2:
581 case M_SOF3:
582 case M_SOF5:
583 case M_SOF6:
584 case M_SOF7:
585 case M_SOF9:
586 case M_SOF10:
587 case M_SOF11:
588 case M_SOF13:
589 case M_SOF14:
590 case M_SOF15:
591 $this->debug("<br>Found M_SOFn Section, Processing<br>");
592 $this->process_SOFn($data,$marker);
593 $this->debug("<br>M_SOFn Section, PROCESSED<br>");
594 break;
595 default:
596 $this->debug("DEFAULT: Jpeg section marker 0x$marker x size $itemlen\n");
597 }
598 $i++;
599 if($exitAll == 1) break;
600 if($tmpTestLevel == 2) break;
601 }
602 fclose($fp);
603 $this->newFile = 0;
604 }
605
606 /**
607 * Changing / Assiging new file
608 * @param string JPEG file to process
609 *
610 */
611 function assign($file) {
612
613 if(!empty($file)) {
614 $this->file = $file;
615 }
616
617 /** check for existance of file! */
618 if(!file_exists($this->file)) {
619 $this->errorno = 1;
620 $this->errorstr = "File '".$this->file."' does not exists!";
621 }
622 $this->newFile = 1;
623 }
624
625 /**
626 * Process SOFn section of Image
627 * @param array An array containing whole section.
628 * @param hex Marker to specify the type of section.
629 *
630 */
631 function process_SOFn($data,$marker) {
632 $data_precision = 0;
633 $num_components = 0;
634
635 $data_precision = ord($data[2]);
636
637 if($this->debug) {
638 print("Image Dimension Calculation:");
639 print("((ord($data[3]) << 8) | ord($data[4]));");
640 }
641 $this->ImageInfo["Height"] = ((ord($data[3]) << 8) | ord($data[4]));
642 $this->ImageInfo["Width"] = ((ord($data[5]) << 8) | ord($data[6]));
643
644 $num_components = ord($data[7]);
645
646 if ($num_components == 3){
647 $this->ImageInfo["IsColor"] = 1;
648 }else{
649 $this->ImageInfo["IsColor"] = 0;
650 }
651
652 $this->ImageInfo["Process"] = $marker;
653 $this->debug("JPEG image is $ImageInfo_Width * $ImageInfo_Height, $num_components color components, $data_precision bits per sample\n");
654 }
655
656 /**
657 * Process Comments
658 * @param array Section data
659 * @param int Length of the section
660 *
661 */
662 function process_COM($data,$length) {
663 if ($length > MAX_COMMENT) $length = MAX_COMMENT;
664 /** Truncate if it won't fit in our structure. */
665
666 $nch = 0;
667 for ($a=2;$a<$length;$a++){
668 $ch = $data[$a];
669 if ($ch == '\r' && $data[$a+1] == '\n') continue; // Remove cr followed by lf.
670
671 $Comment .= $ch;
672 }
673 $this->ImageInfo[M_COM] = $Comment;
674 $this->debug("COM marker comment: $Comment\n");
675 }
676 /**
677 * Process one of the nested EXIF directories.
678 * @param string All directory information
679 * @param string whole Section
680 * @param int Length of exif section
681 *
682 */
683 function ProcessExifDir($DirStart, $OffsetBase, $ExifLength) {
684 global $TagTable
685
686 $NumDirEntries = 0;
687 $ValuePtr = array();
688
689 $NumDirEntries = $this->Get16u($DirStart[0],$DirStart[1]);
690
691
692 $this->debug("<br>Directory with $NumDirEntries entries\n");
693
694 for ($de=0;$de<$NumDirEntries;$de++){
695 $DirEntry = array_slice($DirStart,2+12*$de);
696
697 $Tag = $this->Get16u($DirEntry[0],$DirEntry[1]);
698 $Format = $this->Get16u($DirEntry[2],$DirEntry[3]);
699 $Components = $this->Get32u($DirEntry[4],$DirEntry[5],$DirEntry[6],$DirEntry[7]);
700
701 /**
702 if ((Format-1) >= NUM_FORMATS) {
703 // (-1) catches illegal zero case as unsigned underflows to positive large.
704 ErrNonfatal("Illegal number format %d for tag %04x", Format, Tag);
705 continue;
706 }
707 */
708
709 $ByteCount = $Components * $this->BytesPerFormat[$Format];
710
711 if ($ByteCount > 4){
712 $OffsetVal = $this->Get32u($DirEntry[8],$DirEntry[9],$DirEntry[10],$DirEntry[11]);
713 if ($OffsetVal+$ByteCount > $ExifLength){
714 $this->debug("Illegal value pointer($OffsetVal) for tag $Tag",1);
715 }
716 $ValuePtr = array_slice($OffsetBase,$OffsetVal);
717 } else {
718 $ValuePtr = array_slice($DirEntry,8);
719 }
720
721 /**
722 if (LastExifRefd < ValuePtr+ByteCount){
723 // Keep track of last byte in the exif header that was actually referenced.
724 // That way, we know where the discardable thumbnail data begins.
725 LastExifRefd = ValuePtr+ByteCount;
726 }
727 */
728
729 if($this->showTags) {
730 for ($a=0;;$a++){
731 if ($TagTable[$a][0] == 0){
732 $this->debug(" Unknown Tag $Tag Value = ");
733 break;
734 }
735 if ($TagTable[$a][0] == $Tag){
736 $this->debug(" ".$TagTable[$a][1]." =");
737 break;
738 }
739 }
740
741 switch($Format) {
742 case FMT_UNDEFINED:
743 // Undefined is typically an ascii string.
744
745 case FMT_STRING:
746 // String arrays printed without function call (different from int arrays)
747 {
748 $this->debug("\""); //"
749 $str ="";
750 for ($a=0;$a<$ByteCount;$a++){
751 $str .= $ValuePtr[$a];
752 }
753 $this->debug("$str\"\n"); // "
754
755 }
756 break;
757 default:
758 $this->PrintFormatNumber($ValuePtr, $Format, $ByteCount);
759 } // end of switch
760 } // end of if
761
762 // Extract useful components of tag
763 switch($Tag){
764
765 case TAG_MAKE:
766 $this->ImageInfo[TAG_MAKE]= implode("",array_slice($ValuePtr,0,$ByteCount));
767 break;
768
769 case TAG_MODEL:
770 $this->ImageInfo[TAG_MODEL] = implode("",array_slice($ValuePtr,0,$ByteCount));
771
772 break;
773
774 case TAG_DATETIME_ORIGINAL:
775 $this->ImageInfo[TAG_DATETIME_ORIGINAL] = implode("",array_slice($ValuePtr,0,$ByteCount));
776 $this->ImageInfo["DatePointer"] = implode("",array_slice($ValuePtr,0));
777 break;
778
779 case TAG_USERCOMMENT:
780 // Olympus has this padded with trailing spaces. Remove these first.
781 for ($a=$ByteCount;;){
782 $a--;
783 if ($ValuePtr[$a] == ' '){
784 //$ValuePtr[$a] = '\0';
785 } else {
786 break;
787 }
788 if ($a == 0) break;
789 }
790
791 // Copy the comment
792 if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4]) == "ASCII"){
793 for ($a=5;$a<10;$a++){
794 $c = $ValuePtr[a];
795 if ($c != '\0' && $c != ' '){
796 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount));
797 break;
798 }
799 }
800 } else if (($ValuePtr[0].$ValuePtr[1].$ValuePtr[2].$ValuePtr[3].$ValuePtr[4].$ValuePtr[5].$ValuePtr[6]) == "Unicode"){
801 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount));
802 /**
803 * Handle Unicode characters here...
804 */
805 } else {
806 $this->ImageInfo[TAG_USERCOMMENT] = implode("",array_slice($ValuePtr,0,$ByteCount));
807 }
808 break;
809
810 case TAG_FNUMBER:
811 // Simplest way of expressing aperture, so I trust it the most.
812 // (overwrite previously computd value if there is one)
813 $this->ImageInfo[TAG_FNUMBER] = $this->ConvertAnyFormat(implode("",array_slice($ValuePtr,0)), $Format);
814 break;
815
816 case TAG_APERTURE:
817 case TAG_MAXAPERTURE:
818 // More relevant info always comes earlier, so only use this field if we don't
819 // have appropriate aperture information yet.
820 if ($this->ImageInfo[TAG_MAXAPERTURE] == 0){
821 $tmpArr = $this->ConvertAnyFormat($ValuePtr, $Format);
822 $this->ImageInfo[TAG_MAXAPERTURE] = exp($tmpArr[0]*log(2)*0.5);
823 }
824 break;
825
826 case TAG_FOCALLENGTH:
827 // Nice digital cameras actually save the focal length as a function
828 // of how farthey are zoomed in.
829 $this->ImageInfo[TAG_FOCALLENGTH] = $this->ConvertAnyFormat($ValuePtr, $Format);
830 break;
831
832 case TAG_SUBJECT_DISTANCE:
833 // Inidcates the distacne the autofocus camera is focused to.
834 // Tends to be less accurate as distance increases.
835 $this->ImageInfo[TAG_SUBJECT_DISTANCE] = $this->ConvertAnyFormat($ValuePtr, $Format</