1: <?php
2: 3: 4: 5: 6: 7:
8:
9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
29: class Horde_Image_Exif_Parser_Gps extends Horde_Image_Exif_Parser_Base
30: {
31: 32: 33: 34: 35: 36:
37: protected function _lookupTag($tag)
38: {
39: switch($tag) {
40: case '0000': return 'Version';
41:
42: case '0001': return 'LatitudeRef';
43:
44: case '0002': return 'Latitude';
45:
46: case '0003': return 'LongitudeRef';
47:
48: case '0004': return 'Longitude';
49:
50: case '0005': return 'AltitudeRef';
51:
52: case '0006': return 'Altitude';
53:
54: case '0007': return 'Time';
55:
56: case '0008': return 'Satellite';
57:
58: case '0009': return 'ReceiveStatus';
59:
60: case '000a': return 'MeasurementMode';
61:
62: case '000b': return 'MeasurementPrecision';
63:
64: case '000c': return 'SpeedUnit';
65:
66: case '000d': return 'ReceiverSpeed';
67:
68: case '000e': return 'MovementDirectionRef';
69:
70: case '000f': return 'MovementDirection';
71:
72: case '0010': return 'ImageDirectionRef';
73:
74: case '0011': return 'ImageDirection';
75:
76: case '0012': return 'GeodeticSurveyData';
77:
78: case '0013': return 'DestLatitudeRef';
79:
80: case '0014': return 'DestinationLatitude';
81:
82: case '0015': return 'DestLongitudeRef';
83:
84: case '0016': return 'DestinationLongitude';
85:
86: case '0017': return 'DestBearingRef';
87:
88: case '0018': return 'DestinationBearing';
89:
90: case '0019': return 'DestDistanceRef';
91:
92: case '001a': return 'DestinationDistance';
93: case '001b': return 'ProcessingMethod';
94: case '001c': return 'AreaInformation';
95:
96: case '001d': return 'Datestamp';
97:
98: case '001e': return 'DifferentialCorrection';
99: default: return 'unknown: ' . $tag;
100: }
101: }
102:
103: 104: 105:
106: protected function _rational($data, $intel)
107: {
108: if ($intel == 1) {
109:
110: $top = hexdec(substr($data, 8, 8));
111: } else {
112:
113: $top = hexdec(substr($data, 0, 8));
114: }
115:
116: if ($intel == 1) {
117: $bottom = hexdec(substr($data, 0, 8));
118: } else {
119: $bottom = hexdec(substr($data, 8, 8));
120: }
121:
122: if ($bottom != 0) {
123: $data = $top / $bottom;
124: } elseif ($top == 0) {
125: $data = 0;
126: } else {
127: $data = $top . '/' . $bottom;
128: }
129:
130: return $data;
131: }
132:
133: 134: 135:
136: protected function _formatData($type, $tag, $intel, $data)
137: {
138: switch ($type) {
139: case 'ASCII':
140:
141: if ($tag == '0001' || $tag == '0003') {
142: $data = ($data{1} == $data{2} && $data{1} == $data{3}) ? $data{0} : $data;
143: }
144: break;
145:
146: case 'URATIONAL':
147: case 'SRATIONAL':
148: $data = bin2hex($data);
149: if ($intel == 1) {
150: $data = Horde_Image_Exif::intel2Moto($data);
151: }
152: if ($intel == 1) {
153:
154: $top = hexdec(substr($data, 8, 8));
155: } else {
156:
157: $top = hexdec(substr($data, 0, 8));
158: }
159:
160: if ($intel == 1) {
161: $bottom = hexdec(substr($data, 0, 8));
162: } else {
163: $bottom = hexdec(substr($data, 8, 8));
164: }
165:
166: if ($type == 'SRATIONAL' && $top > 2147483647) {
167:
168: $top = $top - 4294967296;
169: }
170:
171: switch ($tag) {
172: case '0002':
173: case '0004':
174:
175: if ($intel == 1) {
176: $seconds = $this->_rational(substr($data, 0, 16), $intel);
177: $hour = $this->_rational(substr($data, 32, 16), $intel);
178: } else {
179: $hour = $this->_rational(substr($data, 0, 16), $intel);
180: $seconds = $this->_rational(substr($data, 32, 16), $intel);
181: }
182: $minutes = $this->_rational(substr($data, 16, 16), $intel);
183: $data = array($hour, $minutes, $seconds);
184: break;
185:
186: case '0007':
187:
188: $seconds = $this->_rational(substr($data, 0, 16), $intel);
189: $minutes = $this->_rational(substr($data, 16, 16), $intel);
190: $hour = $this->_rational(substr($data, 32, 16), $intel);
191: $data = $hour . ':' . $minutes . ':' . $seconds;
192: break;
193:
194: default:
195: if ($bottom != 0) {
196: $data = $top / $bottom;
197: } elseif ($top == 0) {
198: $data = 0;
199: } else {
200: $data = $top . '/' . $bottom;
201: }
202: if ($tag == '0006') {
203: $data .= 'm';
204: }
205: break;
206: }
207: break;
208:
209: case 'USHORT':
210: case 'SSHORT':
211: case 'ULONG':
212: case 'SLONG':
213: case 'FLOAT':
214: case 'DOUBLE':
215: $data = bin2hex($data);
216: if ($intel == 1) {
217: $data = Horde_Image_Exif::intel2Moto($data);
218: }
219: $data = hexdec($data);
220: break;
221:
222: case 'UNDEFINED':
223: break;
224:
225: case 'UBYTE':
226: $data = bin2hex($data);
227: if ($intel == 1) {
228: $num = Horde_Image_Exif::intel2Moto($data);
229: }
230: switch ($tag) {
231: case '0000':
232:
233: $data = hexdec(substr($data, 0, 2))
234: . '.' . hexdec(substr($data, 2, 2))
235: . '.' . hexdec(substr($data, 4, 2))
236: . '.'. hexdec(substr($data, 6, 2));
237: break;
238: case '0005':
239:
240: if ($data == '00000000') {
241: $data = 'Above Sea Level';
242: } elseif ($data == '01000000') {
243: $data = 'Below Sea Level';
244: }
245: break;
246: }
247: break;
248:
249: default:
250: $data = bin2hex($data);
251: if ($intel == 1) {
252: $data = Horde_Image_Exif::intel2Moto($data);
253: }
254: break;
255: }
256:
257: return $data;
258: }
259:
260: 261: 262: 263: 264: 265:
266: public function parse($block, &$result, $offset, $seek, $globalOffset)
267: {
268: if ($result['Endien'] == 'Intel') {
269: $intel = 1;
270: } else {
271: $intel = 0;
272: }
273:
274:
275:
276: $v = fseek($seek, $globalOffset + $offset);
277: if ($v == -1) {
278: $result['Errors'] = $result['Errors']++;
279: }
280:
281: $num = bin2hex(fread($seek, 2));
282: if ($intel == 1) {
283: $num = Horde_Image_Exif::intel2Moto($num);
284: }
285: $num = hexdec($num);
286: $result['GPS']['NumTags'] = $num;
287: $block = fread($seek, $num * 12);
288: $place = 0;
289:
290:
291: for ($i = 0; $i < $num; $i++) {
292:
293: $tag = bin2hex(substr($block, $place, 2));
294: $place += 2;
295: if ($intel == 1) {
296: $tag = Horde_Image_Exif::intel2Moto($tag);
297: }
298: $tag_name = $this->_lookupTag($tag);
299:
300:
301: $type = bin2hex(substr($block, $place, 2));
302: $place += 2;
303: if ($intel == 1) {
304: $type = Horde_Image_Exif::intel2Moto($type);
305: }
306: $this->_lookupType($type, $size);
307:
308:
309: $count = bin2hex(substr($block, $place, 4));
310: $place += 4;
311: if ($intel==1) {
312: $count = Horde_Image_Exif::intel2Moto($count);
313: }
314: $bytesofdata = $size * hexdec($count);
315:
316:
317: $value = substr($block, $place, 4);
318: $place += 4;
319:
320: if ($bytesofdata <= 4) {
321: $data = $value;
322: } else {
323: $value = bin2hex($value);
324: if ($intel == 1) {
325: $value = Horde_Image_Exif::intel2Moto($value);
326: }
327:
328:
329: $v = fseek($seek, $globalOffset + hexdec($value));
330: if ($v == 0) {
331: $data = fread($seek, $bytesofdata);
332: } elseif ($v == -1) {
333: $result['Errors'] = $result['Errors']++;
334: }
335: }
336: $result['GPS' . $tag_name] = $this->_formatData($type, $tag, $intel, $data);
337: }
338: }
339: }
340: