1 /*
2
3 gaia_exif.c -- Gaia EXIF support
4
5 version 5.0, 2020 August 1
6
7 Author: Sandro Furieri a.furieri@lqt.it
8
9 ------------------------------------------------------------------------------
10
11 Version: MPL 1.1/GPL 2.0/LGPL 2.1
12
13 The contents of this file are subject to the Mozilla Public License Version
14 1.1 (the "License"); you may not use this file except in compliance with
15 the License. You may obtain a copy of the License at
16 http://www.mozilla.org/MPL/
17
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22
23 The Original Code is the SpatiaLite library
24
25 The Initial Developer of the Original Code is Alessandro Furieri
26
27 Portions created by the Initial Developer are Copyright (C) 2008-2021
28 the Initial Developer. All Rights Reserved.
29
30 Contributor(s):
31
32 Alternatively, the contents of this file may be used under the terms of
33 either the GNU General Public License Version 2 or later (the "GPL"), or
34 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 in which case the provisions of the GPL or the LGPL are applicable instead
36 of those above. If you wish to allow use of your version of this file only
37 under the terms of either the GPL or the LGPL, and not to allow others to
38 use your version of this file under the terms of the MPL, indicate your
39 decision by deleting the provisions above and replace them with the notice
40 and other provisions required by the GPL or the LGPL. If you do not delete
41 the provisions above, a recipient may use your version of this file under
42 the terms of any one of the MPL, the GPL or the LGPL.
43
44 */
45
46 #include <sys/types.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <memory.h>
50 #include <math.h>
51 #include <float.h>
52 #include <string.h>
53
54 #if defined(_WIN32) && !defined(__MINGW32__)
55 #include "config-msvc.h"
56 #else
57 #include "config.h"
58 #endif
59
60 #include <spatialite/sqlite.h>
61
62 #include <spatialite/gaiageo.h>
63 #include <spatialite/gaiaexif.h>
64 #include <spatialite/geopackage.h>
65 #include <spatialite.h>
66
67 #ifdef _WIN32
68 #define strcasecmp _stricmp
69 #endif /* not WIN32 */
70
71 static void
exifTagName(char gps,unsigned short tag_id,char * str,int len)72 exifTagName (char gps, unsigned short tag_id, char *str, int len)
73 {
74 /* returns the canonical name corresponding to an EXIF TAG ID */
75 int l;
76 char *name = "UNKNOWN";
77 if (gps)
78 {
79 switch (tag_id)
80 {
81 case 0x00:
82 name = "GPSVersionID";
83 break;
84 case 0x01:
85 name = "GPSLatitudeRef";
86 break;
87 case 0x02:
88 name = "GPSLatitude";
89 break;
90 case 0x03:
91 name = "GPSLongitudeRef";
92 break;
93 case 0x04:
94 name = "GPSLongitude";
95 break;
96 case 0x05:
97 name = "GPSAltitudeRef";
98 break;
99 case 0x06:
100 name = "GPSAltitude";
101 break;
102 case 0x07:
103 name = "GPSTimeStamp";
104 break;
105 case 0x08:
106 name = "GPSSatellites";
107 break;
108 case 0x09:
109 name = "GPSStatus";
110 break;
111 case 0x0A:
112 name = "GPSMeasureMode";
113 break;
114 case 0x0B:
115 name = "GPSDOP";
116 break;
117 case 0x0C:
118 name = "GPSSpeedRef";
119 break;
120 case 0x0D:
121 name = "GPSSpeed";
122 break;
123 case 0x0E:
124 name = "GPSTrackRef";
125 break;
126 case 0x0F:
127 name = "GPSTrack";
128 break;
129 case 0x10:
130 name = "GPSImgDirectionRef";
131 break;
132 case 0x11:
133 name = "GPSImgDirection";
134 break;
135 case 0x12:
136 name = "GPSMapDatum";
137 break;
138 case 0x13:
139 name = "GPSDestLatitudeRef";
140 break;
141 case 0x14:
142 name = "GPSDestLatitude";
143 break;
144 case 0x15:
145 name = "GPSDestLongitudeRef";
146 break;
147 case 0x16:
148 name = "GPSDestLongitude";
149 break;
150 case 0x17:
151 name = "GPSDestBearingRef";
152 break;
153 case 0x18:
154 name = "GPSDestBearing";
155 break;
156 case 0x19:
157 name = "GPSDestDistanceRef";
158 break;
159 case 0x1A:
160 name = "GPSDestDistance";
161 break;
162 case 0x1B:
163 name = "GPSProcessingMethod";
164 break;
165 case 0x1C:
166 name = "GPSAreaInformation";
167 break;
168 case 0x1D:
169 name = "GPSDateStamp";
170 break;
171 case 0x1E:
172 name = "GPSDifferential";
173 break;
174 };
175 }
176 else
177 {
178 switch (tag_id)
179 {
180 case 0x000B:
181 name = "ACDComment";
182 break;
183 case 0x00FE:
184 name = "NewSubFile";
185 break;
186 case 0x00FF:
187 name = "SubFile";
188 break;
189 case 0x0100:
190 name = "ImageWidth";
191 break;
192 case 0x0101:
193 name = "ImageLength";
194 break;
195 case 0x0102:
196 name = "BitsPerSample";
197 break;
198 case 0x0103:
199 name = "Compression";
200 break;
201 case 0x0106:
202 name = "PhotometricInterpretation";
203 break;
204 case 0x010A:
205 name = "FillOrder";
206 break;
207 case 0x010D:
208 name = "DocumentName";
209 break;
210 case 0x010E:
211 name = "ImageDescription";
212 break;
213 case 0x010F:
214 name = "Make";
215 break;
216 case 0x0110:
217 name = "Model";
218 break;
219 case 0x0111:
220 name = "StripOffsets";
221 break;
222 case 0x0112:
223 name = "Orientation";
224 break;
225 case 0x0115:
226 name = "SamplesPerPixel";
227 break;
228 case 0x0116:
229 name = "RowsPerStrip";
230 break;
231 case 0x0117:
232 name = "StripByteCounts";
233 break;
234 case 0x0118:
235 name = "MinSampleValue";
236 break;
237 case 0x0119:
238 name = "MaxSampleValue";
239 break;
240 case 0x011A:
241 name = "XResolution";
242 break;
243 case 0x011B:
244 name = "YResolution";
245 break;
246 case 0x011C:
247 name = "PlanarConfiguration";
248 break;
249 case 0x011D:
250 name = "PageName";
251 break;
252 case 0x011E:
253 name = "XPosition";
254 break;
255 case 0x011F:
256 name = "YPosition";
257 break;
258 case 0x0120:
259 name = "FreeOffsets";
260 break;
261 case 0x0121:
262 name = "FreeByteCounts";
263 break;
264 case 0x0122:
265 name = "GrayResponseUnit";
266 break;
267 case 0x0123:
268 name = "GrayResponseCurve";
269 break;
270 case 0x0124:
271 name = "T4Options";
272 break;
273 case 0x0125:
274 name = "T6Options";
275 break;
276 case 0x0128:
277 name = "ResolutionUnit";
278 break;
279 case 0x0129:
280 name = "PageNumber";
281 break;
282 case 0x012D:
283 name = "TransferFunction";
284 break;
285 case 0x0131:
286 name = "Software";
287 break;
288 case 0x0132:
289 name = "DateTime";
290 break;
291 case 0x013B:
292 name = "Artist";
293 break;
294 case 0x013C:
295 name = "HostComputer";
296 break;
297 case 0x013D:
298 name = "Predictor";
299 break;
300 case 0x013E:
301 name = "WhitePoint";
302 break;
303 case 0x013F:
304 name = "PrimaryChromaticities";
305 break;
306 case 0x0140:
307 name = "ColorMap";
308 break;
309 case 0x0141:
310 name = "HalfToneHints";
311 break;
312 case 0x0142:
313 name = "TileWidth";
314 break;
315 case 0x0143:
316 name = "TileLength";
317 break;
318 case 0x0144:
319 name = "TileOffsets";
320 break;
321 case 0x0145:
322 name = "TileByteCounts";
323 break;
324 case 0x014A:
325 name = "SubIFD";
326 break;
327 case 0x014C:
328 name = "InkSet";
329 break;
330 case 0x014D:
331 name = "InkNames";
332 break;
333 case 0x014E:
334 name = "NumberOfInks";
335 break;
336 case 0x0150:
337 name = "DotRange";
338 break;
339 case 0x0151:
340 name = "TargetPrinter";
341 break;
342 case 0x0152:
343 name = "ExtraSample";
344 break;
345 case 0x0153:
346 name = "SampleFormat";
347 break;
348 case 0x0154:
349 name = "SMinSampleValue";
350 break;
351 case 0x0155:
352 name = "SMaxSampleValue";
353 break;
354 case 0x0156:
355 name = "TransferRange";
356 break;
357 case 0x0157:
358 name = "ClipPath";
359 break;
360 case 0x0158:
361 name = "XClipPathUnits";
362 break;
363 case 0x0159:
364 name = "YClipPathUnits";
365 break;
366 case 0x015A:
367 name = "Indexed";
368 break;
369 case 0x015B:
370 name = "JPEGTables";
371 break;
372 case 0x015F:
373 name = "OPIProxy";
374 break;
375 case 0x0200:
376 name = "JPEGProc";
377 break;
378 case 0x0201:
379 name = "JPEGInterchangeFormat";
380 break;
381 case 0x0202:
382 name = "JPEGInterchangeFormatLength";
383 break;
384 case 0x0203:
385 name = "JPEGRestartInterval";
386 break;
387 case 0x0205:
388 name = "JPEGLosslessPredictors";
389 break;
390 case 0x0206:
391 name = "JPEGPointTransforms";
392 break;
393 case 0x0207:
394 name = "JPEGQTables";
395 break;
396 case 0x0208:
397 name = "JPEGDCTables";
398 break;
399 case 0x0209:
400 name = "JPEGACTables";
401 break;
402 case 0x0211:
403 name = "YCbCrCoefficients";
404 break;
405 case 0x0212:
406 name = "YCbCrSubSampling";
407 break;
408 case 0x0213:
409 name = "YCbCrPositioning";
410 break;
411 case 0x0214:
412 name = "ReferenceBlackWhite";
413 break;
414 case 0x02BC:
415 name = "ExtensibleMetadataPlatform";
416 break;
417 case 0x0301:
418 name = "Gamma";
419 break;
420 case 0x0302:
421 name = "ICCProfileDescriptor";
422 break;
423 case 0x0303:
424 name = "SRGBRenderingIntent";
425 break;
426 case 0x0320:
427 name = "ImageTitle";
428 break;
429 case 0x5001:
430 name = "ResolutionXUnit";
431 break;
432 case 0x5002:
433 name = "ResolutionYUnit";
434 break;
435 case 0x5003:
436 name = "ResolutionXLengthUnit";
437 break;
438 case 0x5004:
439 name = "ResolutionYLengthUnit";
440 break;
441 case 0x5005:
442 name = "PrintFlags";
443 break;
444 case 0x5006:
445 name = "PrintFlagsVersion";
446 break;
447 case 0x5007:
448 name = "PrintFlagsCrop";
449 break;
450 case 0x5008:
451 name = "PrintFlagsBleedWidth";
452 break;
453 case 0x5009:
454 name = "PrintFlagsBleedWidthScale";
455 break;
456 case 0x500A:
457 name = "HalftoneLPI";
458 break;
459 case 0x500B:
460 name = "HalftoneLPIUnit";
461 break;
462 case 0x500C:
463 name = "HalftoneDegree";
464 break;
465 case 0x500D:
466 name = "HalftoneShape";
467 break;
468 case 0x500E:
469 name = "HalftoneMisc";
470 break;
471 case 0x500F:
472 name = "HalftoneScreen";
473 break;
474 case 0x5010:
475 name = "JPEGQuality";
476 break;
477 case 0x5011:
478 name = "GridSize";
479 break;
480 case 0x5012:
481 name = "ThumbnailFormat";
482 break;
483 case 0x5013:
484 name = "ThumbnailWidth";
485 break;
486 case 0x5014:
487 name = "ThumbnailHeight";
488 break;
489 case 0x5015:
490 name = "ThumbnailColorDepth";
491 break;
492 case 0x5016:
493 name = "ThumbnailPlanes";
494 break;
495 case 0x5017:
496 name = "ThumbnailRawBytes";
497 break;
498 case 0x5018:
499 name = "ThumbnailSize";
500 break;
501 case 0x5019:
502 name = "ThumbnailCompressedSize";
503 break;
504 case 0x501A:
505 name = "ColorTransferFunction";
506 break;
507 case 0x501B:
508 name = "ThumbnailData";
509 break;
510 case 0x5020:
511 name = "ThumbnailImageWidth";
512 break;
513 case 0x5021:
514 name = "ThumbnailImageHeight";
515 break;
516 case 0x5022:
517 name = "ThumbnailBitsPerSample";
518 break;
519 case 0x5023:
520 name = "ThumbnailCompression";
521 break;
522 case 0x5024:
523 name = "ThumbnailPhotometricInterp";
524 break;
525 case 0x5025:
526 name = "ThumbnailImageDescription";
527 break;
528 case 0x5026:
529 name = "ThumbnailEquipMake";
530 break;
531 case 0x5027:
532 name = "ThumbnailEquipModel";
533 break;
534 case 0x5028:
535 name = "ThumbnailStripOffsets";
536 break;
537 case 0x5029:
538 name = "ThumbnailOrientation";
539 break;
540 case 0x502A:
541 name = "ThumbnailSamplesPerPixel";
542 break;
543 case 0x502B:
544 name = "ThumbnailRowsPerStrip";
545 break;
546 case 0x502C:
547 name = "ThumbnailStripBytesCount";
548 break;
549 case 0x502D:
550 name = "ThumbnailResolutionX";
551 break;
552 case 0x502E:
553 name = "ThumbnailResolutionY";
554 break;
555 case 0x502F:
556 name = "ThumbnailPlanarConfig";
557 break;
558 case 0x5030:
559 name = "ThumbnailResolutionUnit";
560 break;
561 case 0x5031:
562 name = "ThumbnailTransferFunction";
563 break;
564 case 0x5032:
565 name = "ThumbnailSoftwareUsed";
566 break;
567 case 0x5033:
568 name = "ThumbnailDateTime";
569 break;
570 case 0x5034:
571 name = "ThumbnailArtist";
572 break;
573 case 0x5035:
574 name = "ThumbnailWhitePoint";
575 break;
576 case 0x5036:
577 name = "ThumbnailPrimaryChromaticities";
578 break;
579 case 0x5037:
580 name = "ThumbnailYCbCrCoefficients";
581 break;
582 case 0x5038:
583 name = "ThumbnailYCbCrSubsampling";
584 break;
585 case 0x5039:
586 name = "ThumbnailYCbCrPositioning";
587 break;
588 case 0x503A:
589 name = "ThumbnailRefBlackWhite";
590 break;
591 case 0x503B:
592 name = "ThumbnailCopyRight";
593 break;
594 case 0x5090:
595 name = "LuminanceTable";
596 break;
597 case 0x5091:
598 name = "ChrominanceTable";
599 break;
600 case 0x5100:
601 name = "FrameDelay";
602 break;
603 case 0x5101:
604 name = "LoopCount";
605 break;
606 case 0x5110:
607 name = "PixelUnit";
608 break;
609 case 0x5111:
610 name = "PixelPerUnitX";
611 break;
612 case 0x5112:
613 name = "PixelPerUnitY";
614 break;
615 case 0x5113:
616 name = "PaletteHistogram";
617 break;
618 case 0x1000:
619 name = "RelatedImageFileFormat";
620 break;
621 case 0x800D:
622 name = "ImageID";
623 break;
624 case 0x80E3:
625 name = "Matteing";
626 break;
627 case 0x80E4:
628 name = "DataType";
629 break;
630 case 0x80E5:
631 name = "ImageDepth";
632 break;
633 case 0x80E6:
634 name = "TileDepth";
635 break;
636 case 0x828D:
637 name = "CFARepeatPatternDim";
638 break;
639 case 0x828E:
640 name = "CFAPattern";
641 break;
642 case 0x828F:
643 name = "BatteryLevel";
644 break;
645 case 0x8298:
646 name = "Copyright";
647 break;
648 case 0x829A:
649 name = "ExposureTime";
650 break;
651 case 0x829D:
652 name = "FNumber";
653 break;
654 case 0x83BB:
655 name = "IPTC/NAA";
656 break;
657 case 0x84E3:
658 name = "IT8RasterPadding";
659 break;
660 case 0x84E5:
661 name = "IT8ColorTable";
662 break;
663 case 0x8649:
664 name = "ImageResourceInformation";
665 break;
666 case 0x8769:
667 name = "Exif IFD Pointer";
668 break;
669 case 0x8773:
670 name = "ICC_Profile";
671 break;
672 case 0x8822:
673 name = "ExposureProgram";
674 break;
675 case 0x8824:
676 name = "SpectralSensitivity";
677 break;
678 case 0x8825:
679 name = "GPSInfo IFD Pointer";
680 break;
681 case 0x8827:
682 name = "ISOSpeedRatings";
683 break;
684 case 0x8828:
685 name = "OECF";
686 break;
687 case 0x9000:
688 name = "ExifVersion";
689 break;
690 case 0x9003:
691 name = "DateTimeOriginal";
692 break;
693 case 0x9004:
694 name = "DateTimeDigitized";
695 break;
696 case 0x9101:
697 name = "ComponentsConfiguration";
698 break;
699 case 0x9102:
700 name = "CompressedBitsPerPixel";
701 break;
702 case 0x9201:
703 name = "ShutterSpeedValue";
704 break;
705 case 0x9202:
706 name = "ApertureValue";
707 break;
708 case 0x9203:
709 name = "BrightnessValue";
710 break;
711 case 0x9204:
712 name = "ExposureBiasValue";
713 break;
714 case 0x9205:
715 name = "MaxApertureValue";
716 break;
717 case 0x9206:
718 name = "SubjectDistance";
719 break;
720 case 0x9207:
721 name = "MeteringMode";
722 break;
723 case 0x9208:
724 name = "LightSource";
725 break;
726 case 0x9209:
727 name = "Flash";
728 break;
729 case 0x920A:
730 name = "FocalLength";
731 break;
732 case 0x920B:
733 case 0xA20B:
734 name = "FlashEnergy";
735 break;
736 case 0x920C:
737 case 0xA20C:
738 name = "SpatialFrequencyResponse";
739 break;
740 case 0x920D:
741 name = "Noise";
742 break;
743 case 0x920E:
744 case 0xA20E:
745 name = "FocalPlaneXResolution";
746 break;
747 case 0x920F:
748 case 0XA20F:
749 name = "FocalPlaneYResolution";
750 break;
751 case 0x9210:
752 case 0xA210:
753 name = "FocalPlaneResolutionUnit";
754 break;
755 case 0x9211:
756 name = "ImageNumber";
757 break;
758 case 0x9212:
759 name = "SecurityClassification";
760 break;
761 case 0x9213:
762 name = "ImageHistory";
763 break;
764 case 0x9214:
765 case 0xA214:
766 name = "SubjectLocation";
767 break;
768 case 0x9215:
769 case 0xA215:
770 name = "ExposureIndex";
771 break;
772 case 0x9216:
773 name = "TIFF/EPStandardID";
774 break;
775 case 0x9217:
776 case 0xA217:
777 name = "SensingMethod";
778 break;
779 case 0x923F:
780 name = "StoNits";
781 break;
782 case 0x927C:
783 name = "MakerNote";
784 break;
785 case 0x9286:
786 name = "UserComment";
787 break;
788 case 0x9290:
789 name = "SubSecTime";
790 break;
791 case 0x9291:
792 name = "SubSecTimeOriginal";
793 break;
794 case 0x9292:
795 name = "SubSecTimeDigitized";
796 break;
797 case 0xA000:
798 name = "FlashpixVersion";
799 break;
800 case 0xA001:
801 name = "ColorSpace";
802 break;
803 case 0xA002:
804 name = "ExifImageWidth";
805 break;
806 case 0xA003:
807 name = "ExifImageLength";
808 break;
809 case 0xA004:
810 name = "RelatedSoundFile";
811 break;
812 case 0xA005:
813 name = "Interoperability IFD Pointer";
814 break;
815 case 0xA20D:
816 name = "Noise";
817 break;
818 case 0xA211:
819 name = "ImageNumber";
820 break;
821 case 0xA212:
822 name = "SecurityClassification";
823 break;
824 case 0xA213:
825 name = "ImageHistory";
826 break;
827 case 0xA216:
828 name = "TIFF/EPStandardID";
829 break;
830 case 0xA300:
831 name = "FileSource";
832 break;
833 case 0xA301:
834 name = "SceneType";
835 break;
836 case 0xA302:
837 name = "CFAPattern";
838 break;
839 case 0xA401:
840 name = "CustomRendered";
841 break;
842 case 0xA402:
843 name = "ExposureMode";
844 break;
845 case 0xA403:
846 name = "WhiteBalance";
847 break;
848 case 0xA404:
849 name = "DigitalZoomRatio";
850 break;
851 case 0xA405:
852 name = "FocalLengthIn35mmFilm";
853 break;
854 case 0xA406:
855 name = "SceneCaptureType";
856 break;
857 case 0xA407:
858 name = "GainControl";
859 break;
860 case 0xA408:
861 name = "Contrast";
862 break;
863 case 0xA409:
864 name = "Saturation";
865 break;
866 case 0xA40A:
867 name = "Sharpness";
868 break;
869 case 0xA40B:
870 name = "DeviceSettingDescription";
871 break;
872 case 0xA40C:
873 name = "SubjectDistanceRange";
874 break;
875 case 0xA420:
876 name = "ImageUniqueID";
877 break;
878 };
879 }
880 l = strlen (name);
881 if (len > l)
882 strcpy (str, name);
883 else
884 {
885 memset (str, '\0', len);
886 memcpy (str, name, len - 1);
887 }
888 }
889
890 static unsigned short
exifImportU16(const unsigned char * p,int little_endian,int little_endian_arch)891 exifImportU16 (const unsigned char *p, int little_endian,
892 int little_endian_arch)
893 {
894 /* fetches an unsigned 16bit int from BLOB respecting declared endiannes */
895 union cvt
896 {
897 unsigned char byte[2];
898 unsigned short short_value;
899 } convert;
900 if (little_endian_arch)
901 {
902 /* Litte-Endian architecture [e.g. x86] */
903 if (!little_endian)
904 {
905 /* Big Endian data */
906 convert.byte[0] = *(p + 1);
907 convert.byte[1] = *(p + 0);
908 }
909 else
910 {
911 /* Little Endian data */
912 convert.byte[0] = *(p + 0);
913 convert.byte[1] = *(p + 1);
914 }
915 }
916 else
917 {
918 /* Big Endian architecture [e.g. PPC] */
919 if (!little_endian)
920 {
921 /* Big Endian data */
922 convert.byte[0] = *(p + 0);
923 convert.byte[1] = *(p + 1);
924 }
925 else
926 {
927 /* Little Endian data */
928 convert.byte[0] = *(p + 1);
929 convert.byte[1] = *(p + 0);
930 }
931 }
932 return convert.short_value;
933 }
934
935 static unsigned int
exifImportU32(const unsigned char * p,int little_endian,int little_endian_arch)936 exifImportU32 (const unsigned char *p, int little_endian,
937 int little_endian_arch)
938 {
939 /* fetches an unsigned 32bit int from BLOB respecting declared endiannes */
940 union cvt
941 {
942 unsigned char byte[4];
943 unsigned int int_value;
944 } convert;
945 if (little_endian_arch)
946 {
947 /* Litte-Endian architecture [e.g. x86] */
948 if (!little_endian)
949 {
950 /* Big Endian data */
951 convert.byte[0] = *(p + 3);
952 convert.byte[1] = *(p + 2);
953 convert.byte[2] = *(p + 1);
954 convert.byte[3] = *(p + 0);
955 }
956 else
957 {
958 /* Little Endian data */
959 convert.byte[0] = *(p + 0);
960 convert.byte[1] = *(p + 1);
961 convert.byte[2] = *(p + 2);
962 convert.byte[3] = *(p + 3);
963 }
964 }
965 else
966 {
967 /* Big Endian architecture [e.g. PPC] */
968 if (!little_endian)
969 {
970 /* Big Endian data */
971 convert.byte[0] = *(p + 0);
972 convert.byte[1] = *(p + 1);
973 convert.byte[2] = *(p + 2);
974 convert.byte[3] = *(p + 3);
975 }
976 else
977 {
978 /* Little Endian data */
979 convert.byte[0] = *(p + 3);
980 convert.byte[1] = *(p + 2);
981 convert.byte[2] = *(p + 1);
982 convert.byte[3] = *(p + 0);
983 }
984 }
985 return convert.int_value;
986 }
987
988 static float
exifImportFloat32(const unsigned char * p,int little_endian,int little_endian_arch)989 exifImportFloat32 (const unsigned char *p, int little_endian,
990 int little_endian_arch)
991 {
992 /* fetches a 32bit FLOAT from BLOB respecting declared endiannes */
993 union cvt
994 {
995 unsigned char byte[4];
996 float float_value;
997 } convert;
998 if (little_endian_arch)
999 {
1000 /* Litte-Endian architecture [e.g. x86] */
1001 if (!little_endian)
1002 {
1003 /* Big Endian data */
1004 convert.byte[0] = *(p + 3);
1005 convert.byte[1] = *(p + 2);
1006 convert.byte[2] = *(p + 1);
1007 convert.byte[3] = *(p + 0);
1008 }
1009 else
1010 {
1011 /* Little Endian data */
1012 convert.byte[0] = *(p + 0);
1013 convert.byte[1] = *(p + 1);
1014 convert.byte[2] = *(p + 2);
1015 convert.byte[3] = *(p + 3);
1016 }
1017 }
1018 else
1019 {
1020 /* Big Endian architecture [e.g. PPC] */
1021 if (!little_endian)
1022 {
1023 /* Big Endian data */
1024 convert.byte[0] = *(p + 0);
1025 convert.byte[1] = *(p + 1);
1026 convert.byte[2] = *(p + 2);
1027 convert.byte[3] = *(p + 3);
1028 }
1029 else
1030 {
1031 /* Little Endian data */
1032 convert.byte[0] = *(p + 3);
1033 convert.byte[1] = *(p + 2);
1034 convert.byte[2] = *(p + 1);
1035 convert.byte[3] = *(p + 0);
1036 }
1037 }
1038 return convert.float_value;
1039 }
1040
1041 static void
exifSetTagValue(gaiaExifTagPtr tag,const unsigned char * blob,int endian_mode,int endian_arch,int app1_offset)1042 exifSetTagValue (gaiaExifTagPtr tag, const unsigned char *blob, int endian_mode,
1043 int endian_arch, int app1_offset)
1044 {
1045 /* setting the TAG value */
1046 int i;
1047 int sz = 0;
1048 unsigned int offset;
1049 const unsigned char *ptr;
1050 unsigned short short_value;
1051 unsigned int int_value;
1052 short sign_short_value;
1053 int sign_int_value;
1054 float float_value;
1055 double double_value;
1056 if (tag->Type == 1 || tag->Type == 2 || tag->Type == 6 || tag->Type == 7)
1057 sz = tag->Count;
1058 if (tag->Type == 3 || tag->Type == 8)
1059 sz = tag->Count * 2;
1060 if (tag->Type == 4 || tag->Type == 9 || tag->Type == 11)
1061 sz = tag->Count * 4;
1062 if (tag->Type == 5 || tag->Type == 10 || tag->Type == 12)
1063 sz = tag->Count * 8;
1064 if (sz <= 4)
1065 {
1066 /* TAG values is stored within the offset */
1067 ptr = tag->TagOffset;
1068 }
1069 else
1070 {
1071 /* jumping to offset */
1072 offset = exifImportU32 (tag->TagOffset, endian_mode, endian_arch);
1073 offset += app1_offset + 10;
1074 ptr = blob + offset;
1075 }
1076 if (tag->Type == 1 || tag->Type == 6 || tag->Type == 7)
1077 {
1078 /* BYTE type */
1079 tag->ByteValue = malloc (tag->Count);
1080 memcpy (tag->ByteValue, ptr, tag->Count);
1081 }
1082 if (tag->Type == 2)
1083 {
1084 /* STRING type */
1085 tag->StringValue = malloc (tag->Count);
1086 memcpy (tag->StringValue, ptr, tag->Count);
1087 }
1088 if (tag->Type == 3)
1089 {
1090 /* SHORT type */
1091 tag->ShortValues = malloc (tag->Count * sizeof (unsigned short));
1092 for (i = 0; i < tag->Count; i++)
1093 {
1094 short_value =
1095 exifImportU16 (ptr + (i * 2), endian_mode, endian_arch);
1096 *(tag->ShortValues + i) = short_value;
1097 }
1098 }
1099 if (tag->Type == 4)
1100 {
1101 /* LONG type */
1102 tag->LongValues = malloc (tag->Count * sizeof (unsigned int));
1103 for (i = 0; i < tag->Count; i++)
1104 {
1105 int_value =
1106 exifImportU32 (ptr + (i * 4), endian_mode, endian_arch);
1107 *(tag->LongValues + i) = int_value;
1108 }
1109 }
1110 if (tag->Type == 5)
1111 {
1112 /* RATIONAL type */
1113 tag->LongRationals1 = malloc (tag->Count * sizeof (unsigned int));
1114 tag->LongRationals2 = malloc (tag->Count * sizeof (unsigned int));
1115 for (i = 0; i < tag->Count; i++)
1116 {
1117 int_value =
1118 exifImportU32 (ptr + (i * 8), endian_mode, endian_arch);
1119 *(tag->LongRationals1 + i) = int_value;
1120 int_value =
1121 exifImportU32 (ptr + (i * 8) + 4, endian_mode, endian_arch);
1122 *(tag->LongRationals2 + i) = int_value;
1123 }
1124 }
1125 if (tag->Type == 8)
1126 {
1127 /* SSHORT type */
1128 tag->SignedShortValues = malloc (tag->Count * sizeof (short));
1129 for (i = 0; i < tag->Count; i++)
1130 {
1131 sign_short_value =
1132 gaiaImport16 (ptr + (i * 2), endian_mode, endian_arch);
1133 *(tag->SignedShortValues + i) = sign_short_value;
1134 }
1135 }
1136 if (tag->Type == 9)
1137 {
1138 /* SIGNED LONG type */
1139 tag->SignedLongValues = malloc (tag->Count * sizeof (int));
1140 for (i = 0; i < tag->Count; i++)
1141 {
1142 sign_int_value =
1143 gaiaImport32 (ptr + (i * 4), endian_mode, endian_arch);
1144 *(tag->SignedLongValues + i) = sign_int_value;
1145 }
1146 }
1147 if (tag->Type == 10)
1148 {
1149 /* SIGNED RATIONAL type */
1150 tag->SignedLongRationals1 = malloc (tag->Count * sizeof (int));
1151 tag->SignedLongRationals2 = malloc (tag->Count * sizeof (int));
1152 for (i = 0; i < tag->Count; i++)
1153 {
1154 sign_int_value =
1155 gaiaImport32 (ptr + (i * 8), endian_mode, endian_arch);
1156 *(tag->SignedLongRationals1 + i) = sign_int_value;
1157 sign_int_value =
1158 gaiaImport32 (ptr + (i * 8) + 4, endian_mode, endian_arch);
1159 *(tag->SignedLongRationals2 + i) = sign_int_value;
1160 }
1161 }
1162 if (tag->Type == 11)
1163 {
1164 /* FLOAT type */
1165 tag->FloatValues = malloc (tag->Count * sizeof (float));
1166 for (i = 0; i < tag->Count; i++)
1167 {
1168 float_value =
1169 exifImportFloat32 (ptr + (i * 4), endian_mode, endian_arch);
1170 *(tag->FloatValues + i) = float_value;
1171 }
1172 }
1173 if (tag->Type == 12)
1174 {
1175 /* DOUBLE type */
1176 tag->DoubleValues = malloc (tag->Count * sizeof (double));
1177 for (i = 0; i < tag->Count; i++)
1178 {
1179 double_value =
1180 gaiaImport64 (ptr + (i * 8), endian_mode, endian_arch);
1181 *(tag->DoubleValues + i) = double_value;
1182 }
1183 }
1184 }
1185
1186 static void
exifParseTag(const unsigned char * blob,unsigned int offset,int endian_mode,int endian_arch,gaiaExifTagListPtr list,int gps,int app1_offset)1187 exifParseTag (const unsigned char *blob, unsigned int offset, int endian_mode,
1188 int endian_arch, gaiaExifTagListPtr list, int gps,
1189 int app1_offset)
1190 {
1191 /* parsing some TAG and inserting into the list */
1192 unsigned short tag_id;
1193 unsigned short type;
1194 unsigned int count;
1195 gaiaExifTagPtr tag;
1196 tag_id = exifImportU16 (blob + offset, endian_mode, endian_arch);
1197 type = exifImportU16 (blob + offset + 2, endian_mode, endian_arch);
1198 count = exifImportU32 (blob + offset + 4, endian_mode, endian_arch);
1199 tag = malloc (sizeof (gaiaExifTag));
1200 tag->Gps = (char) gps;
1201 tag->TagId = tag_id;
1202 tag->Type = type;
1203 tag->Count = (unsigned short) count;
1204 memcpy (tag->TagOffset, blob + offset + 8, 4);
1205 tag->ByteValue = NULL;
1206 tag->StringValue = NULL;
1207 tag->ShortValues = NULL;
1208 tag->LongValues = NULL;
1209 tag->LongRationals1 = NULL;
1210 tag->LongRationals2 = NULL;
1211 tag->SignedShortValues = NULL;
1212 tag->SignedLongValues = NULL;
1213 tag->SignedLongRationals1 = NULL;
1214 tag->SignedLongRationals2 = NULL;
1215 tag->FloatValues = NULL;
1216 tag->DoubleValues = NULL;
1217 exifSetTagValue (tag, blob, endian_mode, endian_arch, app1_offset);
1218 tag->Next = NULL;
1219 if (!(list->First))
1220 list->First = tag;
1221 if (list->Last)
1222 (list->Last)->Next = tag;
1223 list->Last = tag;
1224 (list->NumTags)++;
1225 }
1226
1227 static void
exifExpandIFD(gaiaExifTagListPtr list,const unsigned char * blob,int endian_mode,int endian_arch,int app1_offset)1228 exifExpandIFD (gaiaExifTagListPtr list, const unsigned char *blob,
1229 int endian_mode, int endian_arch, int app1_offset)
1230 {
1231 /* trying to expand the EXIF-IFD */
1232 unsigned int offset;
1233 unsigned short items;
1234 unsigned short i;
1235 gaiaExifTagPtr tag;
1236 if (!list)
1237 return;
1238 tag = list->First;
1239 while (tag)
1240 {
1241 if (tag->TagId == 34665)
1242 {
1243 /* ok, this one is an IFD pointer */
1244 offset =
1245 exifImportU32 (tag->TagOffset, endian_mode, endian_arch);
1246 offset += app1_offset + 10;
1247 items = exifImportU16 (blob + offset, endian_mode, endian_arch);
1248 offset += 2;
1249 for (i = 0; i < items; i++)
1250 {
1251 /* fetching the TAGs */
1252 exifParseTag (blob, offset, endian_mode, endian_arch,
1253 list, 0, app1_offset);
1254 offset += 12;
1255 }
1256 }
1257 tag = tag->Next;
1258 }
1259 }
1260
1261 static void
exifExpandGPS(gaiaExifTagListPtr list,const unsigned char * blob,int endian_mode,int endian_arch,int app1_offset)1262 exifExpandGPS (gaiaExifTagListPtr list, const unsigned char *blob,
1263 int endian_mode, int endian_arch, int app1_offset)
1264 {
1265 /* trying to expand the EXIF-GPS */
1266 unsigned int offset;
1267 unsigned short items;
1268 unsigned short i;
1269 gaiaExifTagPtr tag;
1270 if (!list)
1271 return;
1272 tag = list->First;
1273 while (tag)
1274 {
1275 if (tag->TagId == 34853)
1276 {
1277 /* ok, this one is a GPSinfo-IFD pointer */
1278 offset =
1279 exifImportU32 (tag->TagOffset, endian_mode, endian_arch);
1280 offset += app1_offset + 10;
1281 items = exifImportU16 (blob + offset, endian_mode, endian_arch);
1282 offset += 2;
1283 for (i = 0; i < items; i++)
1284 {
1285 /* fetching the TAGs */
1286 exifParseTag (blob, offset, endian_mode, endian_arch,
1287 list, 1, app1_offset);
1288 offset += 12;
1289 }
1290 }
1291 tag = tag->Next;
1292 }
1293 }
1294
1295 GAIAEXIF_DECLARE gaiaExifTagListPtr
gaiaGetExifTags(const unsigned char * blob,int size)1296 gaiaGetExifTags (const unsigned char *blob, int size)
1297 {
1298 /* trying to parse a BLOB as an EXIF photo */
1299 gaiaExifTagListPtr list;
1300 int endian_arch = gaiaEndianArch ();
1301 int endian_mode;
1302 unsigned short app1_size;
1303 unsigned int offset;
1304 unsigned short items;
1305 unsigned short i;
1306 int app1_offset;
1307 gaiaExifTagPtr pT;
1308 if (!blob)
1309 goto error;
1310 if (size < 14)
1311 goto error;
1312 /* checking for SOI [Start Of Image] */
1313 if (*(blob + 0) == 0xff && *(blob + 1) == 0xd8)
1314 ;
1315 else
1316 goto error;
1317 for (app1_offset = 2; app1_offset < size - 1; app1_offset++)
1318 {
1319 if (*(blob + app1_offset) == 0xff
1320 && *(blob + app1_offset + 1) == 0xe1)
1321 {
1322 /* found APP1 marker */
1323 break;
1324 }
1325 }
1326 if (app1_offset == size - 1)
1327 {
1328 /* we've reached the end of the file, but not found the marker */
1329 goto error;
1330 }
1331 /* checking for EXIF identifier */
1332 if (memcmp (blob + app1_offset + 4, "Exif", 4) == 0)
1333 ;
1334 else
1335 goto error;
1336 /* checking for Pad */
1337 if (*(blob + app1_offset + 8) == 0x00 && *(blob + app1_offset + 9) == 0x00)
1338 ;
1339 else
1340 goto error;
1341 if (memcmp (blob + app1_offset + 10, "II", 2) == 0)
1342 endian_mode = GAIA_LITTLE_ENDIAN;
1343 else if (memcmp (blob + app1_offset + 10, "MM", 2) == 0)
1344 endian_mode = GAIA_BIG_ENDIAN;
1345 else
1346 goto error;
1347 /* OK: this BLOB seems to contain a valid EXIF */
1348 app1_size =
1349 exifImportU16 (blob + app1_offset + 2, endian_mode, endian_arch);
1350 if ((app1_size + app1_offset + 4) > size)
1351 goto error;
1352 /* checking for marker */
1353 if (endian_mode == GAIA_BIG_ENDIAN)
1354 {
1355 if (*(blob + app1_offset + 12) == 0x00
1356 && *(blob + app1_offset + 13) == 0x2a)
1357 ;
1358 else
1359 goto error;
1360 }
1361 else
1362 {
1363 if (*(blob + app1_offset + 12) == 0x2a
1364 && *(blob + app1_offset + 13) == 0x00)
1365 ;
1366 else
1367 goto error;
1368 }
1369 /* allocating an EXIF TAG LIST */
1370 list = malloc (sizeof (gaiaExifTagList));
1371 list->First = NULL;
1372 list->Last = NULL;
1373 list->NumTags = 0;
1374 list->TagsArray = NULL;
1375 offset = exifImportU32 (blob + app1_offset + 14, endian_mode, endian_arch);
1376 offset += app1_offset + 10;
1377 /* jump to offset */
1378 items = exifImportU16 (blob + offset, endian_mode, endian_arch);
1379 offset += 2;
1380 for (i = 0; i < items; i++)
1381 {
1382 /* fetching the EXIF TAGs */
1383 exifParseTag (blob, offset, endian_mode, endian_arch, list, 0,
1384 app1_offset);
1385 offset += 12;
1386 }
1387 /* expanding the IFD and GPS tags */
1388 exifExpandIFD (list, blob, endian_mode, endian_arch, app1_offset);
1389 exifExpandGPS (list, blob, endian_mode, endian_arch, app1_offset);
1390 if (list->NumTags)
1391 {
1392 /* organizing the EXIF TAGS as an Array */
1393 list->TagsArray = malloc (sizeof (gaiaExifTagPtr) * list->NumTags);
1394 pT = list->First;
1395 i = 0;
1396 while (pT)
1397 {
1398 *(list->TagsArray + i++) = pT;
1399 pT = pT->Next;
1400 }
1401 }
1402 return list;
1403 error:
1404 return NULL;
1405 }
1406
1407 GAIAEXIF_DECLARE void
gaiaExifTagsFree(gaiaExifTagListPtr p)1408 gaiaExifTagsFree (gaiaExifTagListPtr p)
1409 {
1410 /* memory cleanup; freeing the EXIF TAG list */
1411 gaiaExifTagPtr pT;
1412 gaiaExifTagPtr pTn;
1413 if (!p)
1414 return;
1415 pT = p->First;
1416 while (pT)
1417 {
1418 pTn = pT->Next;
1419 if (pT->ByteValue)
1420 free (pT->ByteValue);
1421 if (pT->StringValue)
1422 free (pT->StringValue);
1423 if (pT->ShortValues)
1424 free (pT->ShortValues);
1425 if (pT->LongValues)
1426 free (pT->LongValues);
1427 if (pT->LongRationals1)
1428 free (pT->LongRationals1);
1429 if (pT->LongRationals2)
1430 free (pT->LongRationals2);
1431 if (pT->SignedShortValues)
1432 free (pT->SignedShortValues);
1433 if (pT->SignedLongValues)
1434 free (pT->SignedLongValues);
1435 if (pT->SignedLongRationals1)
1436 free (pT->SignedLongRationals1);
1437 if (pT->SignedLongRationals2)
1438 free (pT->SignedLongRationals2);
1439 if (pT->FloatValues)
1440 free (pT->FloatValues);
1441 if (pT->DoubleValues)
1442 free (pT->DoubleValues);
1443 free (pT);
1444 pT = pTn;
1445 }
1446 if (p->TagsArray)
1447 free (p->TagsArray);
1448 free (p);
1449 }
1450
1451 GAIAEXIF_DECLARE int
gaiaGetExifTagsCount(gaiaExifTagListPtr tag_list)1452 gaiaGetExifTagsCount (gaiaExifTagListPtr tag_list)
1453 {
1454 /* returns the # TAGSs into this list */
1455 return tag_list->NumTags;
1456 }
1457
1458 GAIAEXIF_DECLARE gaiaExifTagPtr
gaiaGetExifTagByPos(gaiaExifTagListPtr tag_list,const int pos)1459 gaiaGetExifTagByPos (gaiaExifTagListPtr tag_list, const int pos)
1460 {
1461 /* returns the Nth TAG from this list */
1462 if (pos >= 0 && pos < tag_list->NumTags)
1463 return *(tag_list->TagsArray + pos);
1464 return NULL;
1465 }
1466
1467 GAIAEXIF_DECLARE gaiaExifTagPtr
gaiaGetExifTagById(const gaiaExifTagListPtr tag_list,const unsigned short tag_id)1468 gaiaGetExifTagById (const gaiaExifTagListPtr tag_list,
1469 const unsigned short tag_id)
1470 {
1471 /* returns a not-GPS TAG identified by its ID */
1472 gaiaExifTagPtr pT = tag_list->First;
1473 while (pT)
1474 {
1475 if (!(pT->Gps) && pT->TagId == tag_id)
1476 return pT;
1477 pT = pT->Next;
1478 }
1479 return NULL;
1480 }
1481
1482 GAIAEXIF_DECLARE gaiaExifTagPtr
gaiaGetExifGpsTagById(const gaiaExifTagListPtr tag_list,const unsigned short tag_id)1483 gaiaGetExifGpsTagById (const gaiaExifTagListPtr tag_list,
1484 const unsigned short tag_id)
1485 {
1486 /* returns a GPS TAG identified by its ID */
1487 gaiaExifTagPtr pT = tag_list->First;
1488 while (pT)
1489 {
1490 if (pT->Gps && pT->TagId == tag_id)
1491 return pT;
1492 pT = pT->Next;
1493 }
1494 return NULL;
1495 }
1496
1497 GAIAEXIF_DECLARE gaiaExifTagPtr
gaiaGetExifTagByName(const gaiaExifTagListPtr tag_list,const char * tag_name)1498 gaiaGetExifTagByName (const gaiaExifTagListPtr tag_list, const char *tag_name)
1499 {
1500 /* returns a TAG identified by its Name */
1501 char name[128];
1502 gaiaExifTagPtr pT = tag_list->First;
1503 while (pT)
1504 {
1505 exifTagName (pT->Gps, pT->TagId, name, 128);
1506 if (strcasecmp (name, tag_name) == 0)
1507 return pT;
1508 pT = pT->Next;
1509 }
1510 return NULL;
1511 }
1512
1513 GAIAEXIF_DECLARE unsigned short
gaiaExifTagGetId(const gaiaExifTagPtr tag)1514 gaiaExifTagGetId (const gaiaExifTagPtr tag)
1515 {
1516 /* returns the TAG ID */
1517 return tag->TagId;
1518 }
1519
1520 GAIAEXIF_DECLARE int
gaiaIsExifGpsTag(const gaiaExifTagPtr tag)1521 gaiaIsExifGpsTag (const gaiaExifTagPtr tag)
1522 {
1523 /* checks if this one is a GPS tag */
1524 return tag->Gps;
1525 }
1526
1527 GAIAEXIF_DECLARE void
gaiaExifTagGetName(const gaiaExifTagPtr tag,char * str,int len)1528 gaiaExifTagGetName (const gaiaExifTagPtr tag, char *str, int len)
1529 {
1530 /* returns the TAG symbolic Name */
1531 exifTagName (tag->Gps, tag->TagId, str, len);
1532 }
1533
1534 GAIAEXIF_DECLARE unsigned short
gaiaExifTagGetValueType(const gaiaExifTagPtr tag)1535 gaiaExifTagGetValueType (const gaiaExifTagPtr tag)
1536 {
1537 /* returns the TAG value Type */
1538 return tag->Type;
1539 }
1540
1541 GAIAEXIF_DECLARE unsigned short
gaiaExifTagGetNumValues(const gaiaExifTagPtr tag)1542 gaiaExifTagGetNumValues (const gaiaExifTagPtr tag)
1543 {
1544 /* returns the # TAG Values */
1545 return tag->Count;
1546 }
1547
1548 GAIAEXIF_DECLARE unsigned char
gaiaExifTagGetByteValue(const gaiaExifTagPtr tag,const int ind,int * ok)1549 gaiaExifTagGetByteValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1550 {
1551 /* returns the Nth Byte value */
1552 if (ind >= 0
1553 && ind <
1554 tag->Count && (tag->Type == 1 || tag->Type == 6 || tag->Type == 7))
1555 {
1556 *ok = 1;
1557 return *(tag->ByteValue + ind);
1558 }
1559 *ok = 0;
1560 return 0;
1561 }
1562
1563 GAIAEXIF_DECLARE void
gaiaExifTagGetStringValue(const gaiaExifTagPtr tag,char * str,int len,int * ok)1564 gaiaExifTagGetStringValue (const gaiaExifTagPtr tag, char *str, int len,
1565 int *ok)
1566 {
1567 /* returns the String value */
1568 int l;
1569 if (tag->Type == 2)
1570 {
1571 *ok = 1;
1572 l = strlen (tag->StringValue);
1573 if (len > l)
1574 strcpy (str, tag->StringValue);
1575 else
1576 {
1577 memset (str, '\0', len);
1578 memcpy (str, tag->StringValue, len - 1);
1579 }
1580 return;
1581 }
1582 *ok = 0;
1583 }
1584
1585 GAIAEXIF_DECLARE unsigned short
gaiaExifTagGetShortValue(const gaiaExifTagPtr tag,const int ind,int * ok)1586 gaiaExifTagGetShortValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1587 {
1588 /* returns the Nth Short value */
1589 if (ind >= 0 && ind < tag->Count && tag->Type == 3)
1590 {
1591 *ok = 1;
1592 return *(tag->ShortValues + ind);
1593 }
1594 *ok = 0;
1595 return 0;
1596 }
1597
1598 GAIAEXIF_DECLARE unsigned int
gaiaExifTagGetLongValue(const gaiaExifTagPtr tag,const int ind,int * ok)1599 gaiaExifTagGetLongValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1600 {
1601 /* returns the Nth Long value */
1602 if (ind >= 0 && ind < tag->Count && tag->Type == 4)
1603 {
1604 *ok = 1;
1605 return *(tag->LongValues + ind);
1606 }
1607 *ok = 0;
1608 return 0;
1609 }
1610
1611 GAIAEXIF_DECLARE unsigned int
gaiaExifTagGetRational1Value(const gaiaExifTagPtr tag,const int ind,int * ok)1612 gaiaExifTagGetRational1Value (const gaiaExifTagPtr tag, const int ind, int *ok)
1613 {
1614 /* returns the Nth Rational (1) value */
1615 if (ind >= 0 && ind < tag->Count && tag->Type == 5)
1616 {
1617 *ok = 1;
1618 return *(tag->LongRationals1 + ind);
1619 }
1620 *ok = 0;
1621 return 0;
1622 }
1623
1624 GAIAEXIF_DECLARE unsigned int
gaiaExifTagGetRational2Value(const gaiaExifTagPtr tag,const int ind,int * ok)1625 gaiaExifTagGetRational2Value (const gaiaExifTagPtr tag, const int ind, int *ok)
1626 {
1627 /* returns the Nth Rational (2) value */
1628 if (ind >= 0 && ind < tag->Count && tag->Type == 5)
1629 {
1630 *ok = 1;
1631 return *(tag->LongRationals2 + ind);
1632 }
1633 *ok = 0;
1634 return 0;
1635 }
1636
1637 GAIAEXIF_DECLARE double
gaiaExifTagGetRationalValue(const gaiaExifTagPtr tag,const int ind,int * ok)1638 gaiaExifTagGetRationalValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1639 {
1640 /* returns the Nth Rational value as Double */
1641 double x;
1642 if (ind >= 0
1643 && ind < tag->Count && tag->Type == 5 && *(tag->LongRationals2 + ind))
1644 {
1645 *ok = 1;
1646 x = (double) (*(tag->LongRationals1 + ind)) /
1647 (double) (*(tag->LongRationals2 + ind));
1648 return x;
1649 }
1650 *ok = 0;
1651 return 0;
1652 }
1653
1654 GAIAEXIF_DECLARE short
gaiaExifTagGetSignedShortValue(const gaiaExifTagPtr tag,const int ind,int * ok)1655 gaiaExifTagGetSignedShortValue (const gaiaExifTagPtr tag, const int ind,
1656 int *ok)
1657 {
1658 /* returns the Nth Signed Short value */
1659 if (ind >= 0 && ind < tag->Count && tag->Type == 8)
1660 {
1661 *ok = 1;
1662 return *(tag->SignedShortValues + ind);
1663 }
1664 *ok = 0;
1665 return 0;
1666 }
1667
1668 GAIAEXIF_DECLARE int
gaiaExifTagGetSignedLongValue(const gaiaExifTagPtr tag,const int ind,int * ok)1669 gaiaExifTagGetSignedLongValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1670 {
1671 /* returns the Nth Signed Long value */
1672 if (ind >= 0 && ind < tag->Count && tag->Type == 9)
1673 {
1674 *ok = 1;
1675 return *(tag->SignedLongValues + ind);
1676 }
1677 *ok = 0;
1678 return 0;
1679 }
1680
1681 GAIAEXIF_DECLARE int
gaiaExifTagGetSignedRational1Value(const gaiaExifTagPtr tag,const int ind,int * ok)1682 gaiaExifTagGetSignedRational1Value (const gaiaExifTagPtr tag, const int ind,
1683 int *ok)
1684 {
1685 /* returns the Nth Signed Rational (1) value */
1686 if (ind >= 0 && ind < tag->Count && tag->Type == 10)
1687 {
1688 *ok = 1;
1689 return *(tag->SignedLongRationals1 + ind);
1690 }
1691 *ok = 0;
1692 return 0;
1693 }
1694
1695 GAIAEXIF_DECLARE int
gaiaExifTagGetSignedRational2Value(const gaiaExifTagPtr tag,const int ind,int * ok)1696 gaiaExifTagGetSignedRational2Value (const gaiaExifTagPtr tag, const int ind,
1697 int *ok)
1698 {
1699 /* returns the Nth Signed Rational (2) value */
1700 if (ind >= 0 && ind < tag->Count && tag->Type == 10)
1701 {
1702 *ok = 1;
1703 return *(tag->SignedLongRationals2 + ind);
1704 }
1705 *ok = 0;
1706 return 0;
1707 }
1708
1709 GAIAEXIF_DECLARE double
gaiaExifTagGetSignedRationalValue(const gaiaExifTagPtr tag,const int ind,int * ok)1710 gaiaExifTagGetSignedRationalValue (const gaiaExifTagPtr tag, const int ind,
1711 int *ok)
1712 {
1713 /* returns the Nth Signed Rational value as Double */
1714 double x;
1715 if (ind >= 0
1716 && ind <
1717 tag->Count && tag->Type == 10 && *(tag->SignedLongRationals2 + ind))
1718 {
1719 *ok = 1;
1720 x = (double) (*(tag->SignedLongRationals1 + ind)) /
1721 (double) (*(tag->SignedLongRationals2 + ind));
1722 return x;
1723 }
1724 *ok = 0;
1725 return 0;
1726 }
1727
1728 GAIAEXIF_DECLARE float
gaiaExifTagGetFloatValue(const gaiaExifTagPtr tag,const int ind,int * ok)1729 gaiaExifTagGetFloatValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1730 {
1731 /* returns the Nth Float value */
1732 if (ind >= 0 && ind < tag->Count && tag->Type == 11)
1733 {
1734 *ok = 1;
1735 return *(tag->FloatValues + ind);
1736 }
1737 *ok = 0;
1738 return 0;
1739 }
1740
1741 GAIAEXIF_DECLARE double
gaiaExifTagGetDoubleValue(const gaiaExifTagPtr tag,const int ind,int * ok)1742 gaiaExifTagGetDoubleValue (const gaiaExifTagPtr tag, const int ind, int *ok)
1743 {
1744 /* returns the Nth Double value */
1745 if (ind >= 0 && ind < tag->Count && tag->Type == 12)
1746 {
1747 *ok = 1;
1748 return *(tag->DoubleValues + ind);
1749 }
1750 *ok = 0;
1751 return 0;
1752 }
1753
1754 GAIAEXIF_DECLARE void
gaiaExifTagGetHumanReadable(const gaiaExifTagPtr tag,char * str,int len,int * ok)1755 gaiaExifTagGetHumanReadable (const gaiaExifTagPtr tag, char *str, int len,
1756 int *ok)
1757 {
1758 /* returns the Human Readable value */
1759 char *human = "";
1760 char dummy[1024];
1761 int l;
1762 int xok;
1763 double dblval;
1764 switch (tag->TagId)
1765 {
1766 case 0x0128: /* ResolutionUnit */
1767 if (tag->Type == 3 && tag->Count == 1)
1768 {
1769 switch (*(tag->ShortValues + 0))
1770 {
1771 case 2:
1772 human = "Inches";
1773 break;
1774 case 3:
1775 human = "Centimeters";
1776 break;
1777 };
1778 }
1779 break;
1780 case 0x8822: /* ExposureProgram */
1781 if (tag->Type == 3 && tag->Count == 1)
1782 {
1783 switch (*(tag->ShortValues + 0))
1784 {
1785 case 0:
1786 human = "Not defined";
1787 break;
1788 case 1:
1789 human = "Manual";
1790 break;
1791 case 2:
1792 human = "Normal program";
1793 break;
1794 case 3:
1795 human = "Aperture priority";
1796 break;
1797 case 4:
1798 human = "Shutter priority";
1799 break;
1800 case 5:
1801 human = "Creative program (biased toward depth of field)";
1802 break;
1803 case 6:
1804 human =
1805 "Action program (biased toward fast shutter speed)";
1806 break;
1807 case 7:
1808 human =
1809 "Portrait mode (for closeup photos with the background out of focus)";
1810 break;
1811 case 8:
1812 human =
1813 "Landscape mode (for landscape photos with the background in focus)";
1814 break;
1815 };
1816 }
1817 break;
1818 case 0xA402: /* ExposureMode */
1819 if (tag->Type == 3 && tag->Count == 1)
1820 {
1821 switch (*(tag->ShortValues + 0))
1822 {
1823 case 0:
1824 human = "Auto exposure";
1825 break;
1826 case 1:
1827 human = "Manual exposure";
1828 break;
1829 case 2:
1830 human = "Auto bracket";
1831 break;
1832 };
1833 }
1834 break;
1835 case 0x0112: /* Orientation */
1836 if (tag->Type == 3 && tag->Count == 1)
1837 {
1838 switch (*(tag->ShortValues + 0))
1839 {
1840 case 1:
1841 human = "Normal";
1842 break;
1843 case 2:
1844 human = "Mirrored";
1845 break;
1846 case 3:
1847 human = "Upsidedown";
1848 break;
1849 case 4:
1850 human = "Upsidedown Mirrored";
1851 break;
1852 case 5:
1853 human = "90 deg Clockwise Mirrored";
1854 break;
1855 case 6:
1856 human = "90 deg Counterclocwise";
1857 break;
1858 case 7:
1859 human = "90 deg Counterclocwise Mirrored";
1860 break;
1861 case 8:
1862 human = "90 deg Mirrored";
1863 break;
1864 };
1865 }
1866 break;
1867 case 0x9207: /* MeteringMode */
1868 if (tag->Type == 3 && tag->Count == 1)
1869 {
1870 switch (*(tag->ShortValues + 0))
1871 {
1872 case 1:
1873 human = "Average";
1874 break;
1875 case 2:
1876 human = "Center Weighted Average";
1877 break;
1878 case 3:
1879 human = "Spot";
1880 break;
1881 case 4:
1882 human = "MultiSpot";
1883 break;
1884 case 5:
1885 human = "MultiSegment";
1886 break;
1887 case 6:
1888 human = "Partial";
1889 break;
1890 case 255:
1891 human = "Other";
1892 break;
1893 };
1894 }
1895 break;
1896 case 0xA403: /* WhiteBalance */
1897 if (tag->Type == 3 && tag->Count == 1)
1898 {
1899 switch (*(tag->ShortValues + 0))
1900 {
1901 case 0:
1902 human = "Auto";
1903 break;
1904 case 1:
1905 human = "Sunny";
1906 break;
1907 case 2:
1908 human = "Cloudy";
1909 break;
1910 case 3:
1911 human = "Tungsten";
1912 break;
1913 case 4:
1914 human = "Fluorescent";
1915 break;
1916 case 5:
1917 human = "Flash";
1918 break;
1919 case 6:
1920 human = "Custom";
1921 break;
1922 case 129:
1923 human = "Manual";
1924 break;
1925 };
1926 }
1927 break;
1928 case 0x9209: /* Flash */
1929 if (tag->Type == 3 && tag->Count == 1)
1930 {
1931 switch (*(tag->ShortValues + 0))
1932 {
1933 case 0:
1934 case 16:
1935 case 24:
1936 case 32:
1937 human = "No Flash";
1938 break;
1939 case 1:
1940 human = "Flash";
1941 break;
1942 case 5:
1943 human = "Flash, strobe return light not detected";
1944 break;
1945 case 7:
1946 human = "Flash, strobe return light detected";
1947 break;
1948 case 9:
1949 human = "Compulsory Flash";
1950 break;
1951 case 13:
1952 human = "Compulsory Flash, Return light not detected";
1953 break;
1954 case 15:
1955 human = "Compulsory Flash, Return light detected";
1956 break;
1957 case 25:
1958 human = "Flash, Auto-Mode";
1959 break;
1960 case 29:
1961 human = "Flash, Auto-Mode, Return light not detected";
1962 break;
1963 case 31:
1964 human = "Flash, Auto-Mode, Return light detected";
1965 break;
1966 case 65:
1967 human = "Red Eye";
1968 break;
1969 case 69:
1970 human = "Red Eye, Return light not detected";
1971 break;
1972 case 71:
1973 human = "Red Eye, Return light detected";
1974 break;
1975 case 73:
1976 human = "Red Eye, Compulsory Flash";
1977 break;
1978 case 77:
1979 human =
1980 "Red Eye, Compulsory Flash, Return light not detected";
1981 break;
1982 case 79:
1983 human =
1984 "Red Eye, Compulsory Flash, Return light detected";
1985 break;
1986 case 89:
1987 human = "Red Eye, Auto-Mode";
1988 break;
1989 case 93:
1990 human = "Red Eye, Auto-Mode, Return light not detected";
1991 break;
1992 case 95:
1993 human = "Red Eye, Auto-Mode, Return light detected";
1994 break;
1995 };
1996 }
1997 break;
1998 case 0xA217: /* SensingMethod */
1999 if (tag->Type == 3 && tag->Count == 1)
2000 {
2001 switch (*(tag->ShortValues + 0))
2002 {
2003 case 1:
2004 human = "Not defined";
2005 break;
2006 case 2:
2007 human = "One Chip Color Area Sensor";
2008 break;
2009 case 3:
2010 human = "Two Chip Color Area Sensor";
2011 break;
2012 case 4:
2013 human = "Three Chip Color Area Sensor";
2014 break;
2015 case 5:
2016 human = "Color Sequential Area Sensor";
2017 break;
2018 case 7:
2019 human = "Trilinear Sensor";
2020 break;
2021 case 8:
2022 human = "Color Sequential Linear Sensor";
2023 break;
2024 };
2025 }
2026 break;
2027 case 0xA406: /* SceneCaptureType */
2028 if (tag->Type == 3 && tag->Count == 1)
2029 {
2030 switch (*(tag->ShortValues + 0))
2031 {
2032 case 0:
2033 human = "Standard";
2034 break;
2035 case 1:
2036 human = "Landscape";
2037 break;
2038 case 2:
2039 human = "Portrait";
2040 break;
2041 case 3:
2042 human = "Night scene";
2043 break;
2044 };
2045 }
2046 break;
2047 case 0xA407: /* GainControl */
2048 if (tag->Type == 3 && tag->Count == 1)
2049 {
2050 switch (*(tag->ShortValues + 0))
2051 {
2052 case 0:
2053 human = "None";
2054 break;
2055 case 1:
2056 human = "Low gain up";
2057 break;
2058 case 2:
2059 human = "High gain up";
2060 break;
2061 case 3:
2062 human = "Low gain down";
2063 break;
2064 case 4:
2065 human = "High gain down";
2066 break;
2067 };
2068 }
2069 break;
2070 case 0xA408: /* Contrast */
2071 if (tag->Type == 3 && tag->Count == 1)
2072 {
2073 switch (*(tag->ShortValues + 0))
2074 {
2075 case 0:
2076 human = "Normal";
2077 break;
2078 case 1:
2079 human = "Soft";
2080 break;
2081 case 2:
2082 human = "Hard";
2083 break;
2084 };
2085 }
2086 break;
2087 case 0xA409: /* Saturation */
2088 if (tag->Type == 3 && tag->Count == 1)
2089 {
2090 switch (*(tag->ShortValues + 0))
2091 {
2092 case 0:
2093 human = "Normal";
2094 break;
2095 case 1:
2096 human = "Low saturation";
2097 break;
2098 case 2:
2099 human = "High saturation";
2100 break;
2101 };
2102 }
2103 break;
2104 case 0xA40A: /* Sharpness */
2105 if (tag->Type == 3 && tag->Count == 1)
2106 {
2107 switch (*(tag->ShortValues + 0))
2108 {
2109 case 0:
2110 human = "Normal";
2111 break;
2112 case 1:
2113 human = "Soft";
2114 break;
2115 case 2:
2116 human = "Hard";
2117 break;
2118 };
2119 }
2120 break;
2121 case 0xA40C: /* SubjectDistanceRange */
2122 if (tag->Type == 3 && tag->Count == 1)
2123 {
2124 switch (*(tag->ShortValues + 0))
2125 {
2126 case 0:
2127 human = "Unknown";
2128 break;
2129 case 1:
2130 human = "Macro";
2131 break;
2132 case 2:
2133 human = "Close view";
2134 break;
2135 case 3:
2136 human = "Distant view";
2137 break;
2138 };
2139 }
2140 break;
2141 case 0x9208: /* LightSource */
2142 if (tag->Type == 3 && tag->Count == 1)
2143 {
2144 switch (*(tag->ShortValues + 0))
2145 {
2146 case 0:
2147 human = "Unknown";
2148 break;
2149 case 1:
2150 human = "Daylight";
2151 break;
2152 case 2:
2153 human = "Fluorescent";
2154 break;
2155 case 3:
2156 human = "Tungsten (incandescent light)";
2157 break;
2158 case 4:
2159 human = "Flash";
2160 break;
2161 case 9:
2162 human = "Fine weather";
2163 break;
2164 case 10:
2165 human = "Cloudy weather";
2166 break;
2167 case 11:
2168 human = "Shade";
2169 break;
2170 case 12:
2171 human = "Daylight fluorescent (D 5700 - 7100K)";
2172 break;
2173 case 13:
2174 human = "Day white fluorescent (N 4600 - 5400K)";
2175 break;
2176 case 14:
2177 human = "Cool white fluorescent (W 3900 - 4500K)";
2178 break;
2179 case 15:
2180 human = "White fluorescent (WW 3200 - 3700K)";
2181 break;
2182 case 17:
2183 human = "Standard light A";
2184 break;
2185 case 18:
2186 human = "Standard light B";
2187 break;
2188 case 19:
2189 human = "Standard light C";
2190 break;
2191 case 20:
2192 human = "D55";
2193 break;
2194 case 21:
2195 human = "D65";
2196 break;
2197 case 22:
2198 human = "D75";
2199 break;
2200 case 23:
2201 human = "D50";
2202 break;
2203 case 24:
2204 human = "ISO studio tungsten";
2205 break;
2206 case 255:
2207 human = "other light source";
2208 break;
2209 };
2210 }
2211 break;
2212 case 0xA001: /* ColorSpace */
2213 if (tag->Type == 3 && tag->Count == 1)
2214 {
2215 switch (*(tag->ShortValues + 0))
2216 {
2217 case 1:
2218 human = "sRGB";
2219 break;
2220 case 0xffff:
2221 human = "Uncalibrated";
2222 break;
2223 };
2224 }
2225 break;
2226 case 0x8827: /* ISOSpeedRatings */
2227 if (tag->Type == 3 && tag->Count == 1)
2228 {
2229 sprintf (dummy, "%u ISO", *(tag->ShortValues + 0));
2230 human = dummy;
2231 }
2232 break;
2233 case 0xA002: /* ExifImageWidth */
2234 case 0xA003: /* ExifImageLength */
2235 if (tag->Type == 3 && tag->Count == 1)
2236 {
2237 sprintf (dummy, "%u pixels", *(tag->ShortValues + 0));
2238 human = dummy;
2239 }
2240 else if (tag->Type == 4 && tag->Count == 1)
2241 {
2242 sprintf (dummy, "%u pixels", *(tag->LongValues + 0));
2243 human = dummy;
2244 }
2245 break;
2246 case 0x829A: /* ExposureTime */
2247 if (tag->Type == 5 && tag->Count == 1)
2248 {
2249 dblval = gaiaExifTagGetRationalValue (tag, 0, &xok);
2250 if (xok)
2251 {
2252 if (dblval < 1.0)
2253 {
2254 dblval = 1.0 / dblval;
2255 sprintf (dummy, "1/%1.0f sec", dblval);
2256 human = dummy;
2257 }
2258 else
2259 {
2260 sprintf (dummy, "%1.0f sec", dblval);
2261 human = dummy;
2262 }
2263 }
2264 }
2265 break;
2266 case 0x9201: /* ShutterSpeedValue */
2267 if (tag->Type == 10 && tag->Count == 1)
2268 {
2269 dblval = gaiaExifTagGetSignedRationalValue (tag, 0, &xok);
2270 if (xok)
2271 {
2272 dblval = exp (dblval * log (2));
2273 if (dblval > 1.0)
2274 dblval = floor (dblval);
2275 if (dblval < 1.0)
2276 {
2277 dblval = math_round (1.0 / dblval);
2278 sprintf (dummy, "%1.0f sec", dblval);
2279 human = dummy;
2280 }
2281 else
2282 {
2283 sprintf (dummy, "1/%1.0f sec", dblval);
2284 human = dummy;
2285 }
2286 }
2287 }
2288 break;
2289 case 0x829D: /* FNumber */
2290 if (tag->Type == 5 && tag->Count == 1)
2291 {
2292 dblval = gaiaExifTagGetRationalValue (tag, 0, &xok);
2293 if (xok)
2294 {
2295 sprintf (dummy, "F %1.1f", dblval);
2296 human = dummy;
2297 }
2298 }
2299 break;
2300 case 0x9202: /* ApertureValue */
2301 case 0x9205: /* MaxApertureValue */
2302 if (tag->Type == 5 && tag->Count == 1)
2303 {
2304 dblval = gaiaExifTagGetRationalValue (tag, 0, &xok);
2305 if (xok)
2306 {
2307 dblval = exp ((dblval * log (2)) / 2.0);
2308 sprintf (dummy, "F %1.1f", dblval);
2309 human = dummy;
2310 }
2311 }
2312 break;
2313 case 0x920A: /* FocalLength */
2314 if (tag->Type == 5 && tag->Count == 1)
2315 {
2316 dblval = gaiaExifTagGetRationalValue (tag, 0, &xok);
2317 if (xok)
2318 {
2319 sprintf (dummy, "%1.1f mm", dblval);
2320 human = dummy;
2321 }
2322 }
2323 break;
2324 case 0xA405: /* FocalLengthIn35mmFilm */
2325 if (tag->Type == 3 && tag->Count == 1)
2326 {
2327 sprintf (dummy, "%u mm", *(tag->ShortValues + 0));
2328 human = dummy;
2329 }
2330 break;
2331 case 0x9204: /* ExposureBiasValue */
2332 if (tag->Type == 10 && tag->Count == 1)
2333 {
2334 dblval = gaiaExifTagGetSignedRationalValue (tag, 0, &xok);
2335 if (xok)
2336 {
2337 sprintf (dummy, "%1.2f EV", dblval);
2338 human = dummy;
2339 }
2340 }
2341 break;
2342 };
2343 l = strlen (human);
2344 if (l > 0)
2345 {
2346 if (len > l)
2347 strcpy (str, human);
2348 else
2349 {
2350 memset (str, '\0', len);
2351 memcpy (str, human, len - 1);
2352 }
2353 *ok = 1;
2354 return;
2355 }
2356 *ok = 0;
2357 }
2358
2359 static int
parse_multi_geom(const unsigned char * blob,int size,int endian,int endian_arch,int * is_compr)2360 parse_multi_geom (const unsigned char *blob, int size, int endian,
2361 int endian_arch, int *is_compr)
2362 {
2363 /* testing for a compressed multi-geometry */
2364 int entities;
2365 int type;
2366 int ie;
2367 int offset = 43;
2368 int points;
2369 int rings;
2370 int ib;
2371 int compressed = 0;
2372 int not_compressed = 0;
2373
2374 if (size < offset + 4)
2375 return 0;
2376 entities = gaiaImport32 (blob + offset, endian, endian_arch);
2377 offset += 4;
2378 for (ie = 0; ie < entities; ie++)
2379 {
2380 if (size < offset + 5)
2381 return 0;
2382 type = gaiaImport32 (blob + offset + 1, endian, endian_arch);
2383 offset += 5;
2384 switch (type)
2385 {
2386 case GAIA_POINT:
2387 if (size < offset + 16)
2388 return 0;
2389 offset += 16;
2390 break;
2391 case GAIA_POINTZ:
2392 if (size < offset + 24)
2393 return 0;
2394 offset += 24;
2395 break;
2396 case GAIA_POINTM:
2397 if (size < offset + 24)
2398 return 0;
2399 offset += 24;
2400 break;
2401 case GAIA_POINTZM:
2402 if (size < offset + 32)
2403 return 0;
2404 offset += 32;
2405 break;
2406 case GAIA_LINESTRING:
2407 if (size < offset + 4)
2408 return 0;
2409 points = gaiaImport32 (blob + offset, endian, endian_arch);
2410 offset += 4 + (points * 16);
2411 not_compressed = 1;
2412 break;
2413 case GAIA_LINESTRINGZ:
2414 if (size < offset + 4)
2415 return 0;
2416 points = gaiaImport32 (blob + offset, endian, endian_arch);
2417 offset += 4 + (points * 24);
2418 not_compressed = 1;
2419 break;
2420 case GAIA_LINESTRINGM:
2421 if (size < offset + 4)
2422 return 0;
2423 points = gaiaImport32 (blob + offset, endian, endian_arch);
2424 offset += 4 + (points * 24);
2425 not_compressed = 1;
2426 break;
2427 case GAIA_LINESTRINGZM:
2428 if (size < offset + 4)
2429 return 0;
2430 points = gaiaImport32 (blob + offset, endian, endian_arch);
2431 offset += 4 + (points * 32);
2432 not_compressed = 1;
2433 break;
2434 case GAIA_POLYGON:
2435 if (size < offset + 4)
2436 return 0;
2437 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2438 offset += 4;
2439 for (ib = 0; ib < rings; ib++)
2440 {
2441 if (size < offset + 4)
2442 return 0;
2443 points =
2444 gaiaImport32 (blob + offset, endian, endian_arch);
2445 offset += 4 + (points * 16);
2446 }
2447 not_compressed = 1;
2448 break;
2449 case GAIA_POLYGONZ:
2450 if (size < offset + 4)
2451 return 0;
2452 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2453 offset += 4;
2454 for (ib = 0; ib < rings; ib++)
2455 {
2456 if (size < offset + 4)
2457 return 0;
2458 points =
2459 gaiaImport32 (blob + offset, endian, endian_arch);
2460 offset += 4 + (points * 24);
2461 }
2462 not_compressed = 1;
2463 break;
2464 case GAIA_POLYGONM:
2465 if (size < offset + 4)
2466 return 0;
2467 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2468 offset += 4;
2469 for (ib = 0; ib < rings; ib++)
2470 {
2471 if (size < offset + 4)
2472 return 0;
2473 points =
2474 gaiaImport32 (blob + offset, endian, endian_arch);
2475 offset += 4 + (points * 24);
2476 }
2477 not_compressed = 1;
2478 break;
2479 case GAIA_POLYGONZM:
2480 if (size < offset + 4)
2481 return 0;
2482 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2483 offset += 4;
2484 for (ib = 0; ib < rings; ib++)
2485 {
2486 if (size < offset + 4)
2487 return 0;
2488 points =
2489 gaiaImport32 (blob + offset, endian, endian_arch);
2490 offset += 4 + (points * 32);
2491 }
2492 not_compressed = 1;
2493 break;
2494 case GAIA_COMPRESSED_LINESTRING:
2495 if (size < offset + 4)
2496 return 0;
2497 points = gaiaImport32 (blob + offset, endian, endian_arch);
2498 offset += 4 + (points * 8) + 16;
2499 compressed = 1;
2500 break;
2501 case GAIA_COMPRESSED_LINESTRINGZ:
2502 if (size < offset + 4)
2503 return 0;
2504 points = gaiaImport32 (blob + offset, endian, endian_arch);
2505 offset += 4 + (points * 12) + 24;
2506 compressed = 1;
2507 break;
2508 case GAIA_COMPRESSED_LINESTRINGM:
2509 if (size < offset + 4)
2510 return 0;
2511 points = gaiaImport32 (blob + offset, endian, endian_arch);
2512 offset += 4 + (points * 16) + 16;
2513 compressed = 1;
2514 break;
2515 case GAIA_COMPRESSED_LINESTRINGZM:
2516 if (size < offset + 4)
2517 return 0;
2518 points = gaiaImport32 (blob + offset, endian, endian_arch);
2519 offset += 4 + (points * 20) + 24;
2520 compressed = 1;
2521 break;
2522 case GAIA_COMPRESSED_POLYGON:
2523 if (size < offset + 4)
2524 return 0;
2525 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2526 offset += 4;
2527 for (ib = 0; ib < rings; ib++)
2528 {
2529 if (size < offset + 4)
2530 return 0;
2531 points =
2532 gaiaImport32 (blob + offset, endian, endian_arch);
2533 offset += 4 + (points * 8) + 16;
2534 }
2535 compressed = 1;
2536 break;
2537 case GAIA_COMPRESSED_POLYGONZ:
2538 if (size < offset + 4)
2539 return 0;
2540 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2541 offset += 4;
2542 for (ib = 0; ib < rings; ib++)
2543 {
2544 if (size < offset + 4)
2545 return 0;
2546 points =
2547 gaiaImport32 (blob + offset, endian, endian_arch);
2548 offset += 4 + (points * 12) + 24;
2549 }
2550 compressed = 1;
2551 break;
2552 case GAIA_COMPRESSED_POLYGONM:
2553 if (size < offset + 4)
2554 return 0;
2555 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2556 offset += 4;
2557 for (ib = 0; ib < rings; ib++)
2558 {
2559 if (size < offset + 4)
2560 return 0;
2561 points =
2562 gaiaImport32 (blob + offset, endian, endian_arch);
2563 offset += 4 + (points * 16) + 16;
2564 }
2565 compressed = 1;
2566 break;
2567 case GAIA_COMPRESSED_POLYGONZM:
2568 if (size < offset + 4)
2569 return 0;
2570 rings = gaiaImport32 (blob + offset, endian, endian_arch);
2571 offset += 4;
2572 for (ib = 0; ib < rings; ib++)
2573 {
2574 if (size < offset + 4)
2575 return 0;
2576 points =
2577 gaiaImport32 (blob + offset, endian, endian_arch);
2578 offset += 4 + (points * 20) + 24;
2579 }
2580 compressed = 1;
2581 break;
2582 default:
2583 return 0;
2584 };
2585 }
2586 if (compressed && !not_compressed)
2587 *is_compr = 1;
2588 else
2589 *is_compr = 0;
2590 return 1;
2591 }
2592
2593 GAIAEXIF_DECLARE int
gaiaGuessBlobType(const unsigned char * blob,int size)2594 gaiaGuessBlobType (const unsigned char *blob, int size)
2595 {
2596 /* returns the BLOB content type */
2597 int jpeg = 0;
2598 int exif = 0;
2599 int exif_gps = 0;
2600 int geom = 1;
2601 int tiny_point = 1;
2602 gaiaExifTagListPtr exif_list;
2603 gaiaExifTagPtr pT;
2604 unsigned char jpeg1_signature[2];
2605 unsigned char jpeg2_signature[2];
2606 unsigned char jpeg3_signature[4];
2607 unsigned char jfif_signature[4];
2608 unsigned char exif_signature[4];
2609 unsigned char png_signature[8];
2610 unsigned char zip_signature[4];
2611 unsigned char tiff_signature_little[4];
2612 unsigned char tiff_signature_big[4];
2613 unsigned char riff_signature[4];
2614 unsigned char webp_signature[8];
2615 unsigned char jp2_little[12];
2616 unsigned char jp2_big[12];
2617 jpeg1_signature[0] = 0xff;
2618 jpeg1_signature[1] = 0xd8;
2619 jpeg2_signature[0] = 0xff;
2620 jpeg2_signature[1] = 0xd9;
2621 jpeg3_signature[0] = 0xff;
2622 jpeg3_signature[1] = 0xd8;
2623 jpeg3_signature[2] = 0xff;
2624 jpeg3_signature[3] = 0xe0;
2625 jfif_signature[0] = 0x4a;
2626 jfif_signature[1] = 0x46;
2627 jfif_signature[2] = 0x49;
2628 jfif_signature[3] = 0x46;
2629 exif_signature[0] = 0x45;
2630 exif_signature[1] = 0x78;
2631 exif_signature[2] = 0x69;
2632 exif_signature[3] = 0x66;
2633 png_signature[0] = 0x89;
2634 png_signature[1] = 0x50;
2635 png_signature[2] = 0x4e;
2636 png_signature[3] = 0x47;
2637 png_signature[4] = 0x0d;
2638 png_signature[5] = 0x0a;
2639 png_signature[6] = 0x1a;
2640 png_signature[7] = 0x0a;
2641 zip_signature[0] = 0x50;
2642 zip_signature[1] = 0x4b;
2643 zip_signature[2] = 0x03;
2644 zip_signature[3] = 0x04;
2645 tiff_signature_little[0] = 'I';
2646 tiff_signature_little[1] = 'I';
2647 tiff_signature_little[2] = 0x2a;
2648 tiff_signature_little[3] = 0x00;
2649 tiff_signature_big[0] = 'M';
2650 tiff_signature_big[1] = 'M';
2651 tiff_signature_big[2] = 0x00;
2652 tiff_signature_big[3] = 0x2a;
2653 riff_signature[0] = 'R';
2654 riff_signature[1] = 'I';
2655 riff_signature[2] = 'F';
2656 riff_signature[3] = 'F';
2657 webp_signature[0] = 'W';
2658 webp_signature[1] = 'E';
2659 webp_signature[2] = 'B';
2660 webp_signature[3] = 'P';
2661 webp_signature[4] = 'V';
2662 webp_signature[5] = 'P';
2663 webp_signature[6] = '8';
2664 webp_signature[7] = ' ';
2665 jp2_big[0] = 0x00;
2666 jp2_big[1] = 0x00;
2667 jp2_big[2] = 0x00;
2668 jp2_big[3] = 0x0C;
2669 jp2_big[4] = 0x6A;
2670 jp2_big[5] = 0x50;
2671 jp2_big[6] = 0x20;
2672 jp2_big[7] = 0x20;
2673 jp2_big[8] = 0x0D;
2674 jp2_big[9] = 0x0A;
2675 jp2_big[10] = 0x87;
2676 jp2_big[11] = 0x0A;
2677 jp2_little[0] = 0x00;
2678 jp2_little[1] = 0x00;
2679 jp2_little[2] = 0x0c;
2680 jp2_little[3] = 0x00;
2681 jp2_little[4] = 0x50;
2682 jp2_little[5] = 0x6a;
2683 jp2_little[6] = 0x20;
2684 jp2_little[7] = 0x20;
2685 jp2_little[8] = 0x0a;
2686 jp2_little[9] = 0x0d;
2687 jp2_little[10] = 0x0a;
2688 jp2_little[11] = 0x87;
2689 if (size < 1 || !blob)
2690 return GAIA_HEX_BLOB;
2691 if (size > 4)
2692 {
2693 if (memcmp (blob, tiff_signature_big, 4) == 0)
2694 return GAIA_TIFF_BLOB;
2695 if (memcmp (blob, tiff_signature_little, 4) == 0)
2696 return GAIA_TIFF_BLOB;
2697 }
2698 if (size > 5)
2699 {
2700 if (strncmp ((char *) blob, "%PDF-", 5) == 0)
2701 return GAIA_PDF_BLOB;
2702 }
2703 if (size > 4)
2704 {
2705 if (memcmp (blob, zip_signature, 4) == 0)
2706 return GAIA_ZIP_BLOB;
2707 }
2708 if (size > 6)
2709 {
2710 if (strncmp ((char *) blob, "GIF87a", 6) == 0
2711 || strncmp ((char *) blob, "GIF89a", 6) == 0)
2712 return GAIA_GIF_BLOB;
2713 }
2714 if (size > 8)
2715 {
2716 if (memcmp (blob, png_signature, 8) == 0)
2717 return GAIA_PNG_BLOB;
2718 }
2719 if (size > 12)
2720 {
2721 if (memcmp (blob, jp2_big, 12) == 0)
2722 return GAIA_JP2_BLOB;
2723 if (memcmp (blob, jp2_little, 12) == 0)
2724 return GAIA_JP2_BLOB;
2725 }
2726 if (size > 4)
2727 {
2728 if (memcmp (blob, jpeg1_signature, 2) == 0
2729 && memcmp (blob + size - 2, jpeg2_signature, 2) == 0)
2730 jpeg = 1; /* this one is the standard JPEG signature */
2731 if (memcmp (blob, jpeg3_signature, 4) == 0)
2732 jpeg = 1; /* another common JPEG signature */
2733 }
2734 if (size > 10)
2735 {
2736 if (memcmp (blob + 6, jfif_signature, 4) == 0)
2737 jpeg = 1; /* standard JFIF signature */
2738 if (memcmp (blob + 6, exif_signature, 4) == 0)
2739 jpeg = 1; /* standard EXIF signature */
2740 }
2741 if (jpeg)
2742 {
2743 exif_list = gaiaGetExifTags (blob, size);
2744 if (exif_list)
2745 {
2746 exif = 1;
2747 pT = exif_list->First;
2748 while (pT)
2749 {
2750 if (pT->Gps)
2751 {
2752 exif_gps = 1;
2753 break;
2754 }
2755 pT = pT->Next;
2756 }
2757 gaiaExifTagsFree (exif_list);
2758 }
2759 }
2760 if (jpeg && exif && exif_gps)
2761 return GAIA_EXIF_GPS_BLOB;
2762 if (jpeg && exif)
2763 return GAIA_EXIF_BLOB;
2764 if (jpeg)
2765 return GAIA_JPEG_BLOB;
2766 if (size > 16)
2767 {
2768 if ((memcmp (blob, riff_signature, 4) == 0) &&
2769 (memcmp (blob + 8, webp_signature, 8) == 0))
2770 return GAIA_WEBP_BLOB;
2771 }
2772 /* testing for GEOMETRY */
2773 if (size < 45)
2774 geom = 0;
2775 else
2776 {
2777 if (*(blob + 0) != GAIA_MARK_START)
2778 geom = 0;
2779 if (*(blob + 1) == GAIA_LITTLE_ENDIAN
2780 || *(blob + 1) == GAIA_BIG_ENDIAN)
2781 ;
2782 else
2783 geom = 0;
2784 if (*(blob + (size - 1)) != GAIA_MARK_END)
2785 geom = 0;
2786 if (*(blob + 38) != GAIA_MARK_MBR)
2787 geom = 0;
2788 }
2789 if (geom)
2790 {
2791 int little_endian;
2792 int endian_arch = gaiaEndianArch ();
2793 int gtype;
2794 int is_compr;
2795 if (*(blob + 1) == GAIA_LITTLE_ENDIAN)
2796 little_endian = 1;
2797 else if (*(blob + 1) == GAIA_BIG_ENDIAN)
2798 little_endian = 0;
2799 else
2800 goto not_a_valid_geom;
2801 gtype = gaiaImport32 (blob + 39, little_endian, endian_arch);
2802 switch (gtype)
2803 {
2804 case GAIA_COMPRESSED_LINESTRING:
2805 case GAIA_COMPRESSED_LINESTRINGZ:
2806 case GAIA_COMPRESSED_LINESTRINGM:
2807 case GAIA_COMPRESSED_LINESTRINGZM:
2808 case GAIA_COMPRESSED_POLYGON:
2809 case GAIA_COMPRESSED_POLYGONZ:
2810 case GAIA_COMPRESSED_POLYGONM:
2811 case GAIA_COMPRESSED_POLYGONZM:
2812 return GAIA_COMPRESSED_GEOMETRY_BLOB;
2813 case GAIA_MULTILINESTRING:
2814 case GAIA_MULTILINESTRINGZ:
2815 case GAIA_MULTILINESTRINGM:
2816 case GAIA_MULTILINESTRINGZM:
2817 case GAIA_MULTIPOLYGON:
2818 case GAIA_MULTIPOLYGONZ:
2819 case GAIA_MULTIPOLYGONM:
2820 case GAIA_MULTIPOLYGONZM:
2821 case GAIA_GEOMETRYCOLLECTION:
2822 case GAIA_GEOMETRYCOLLECTIONZ:
2823 case GAIA_GEOMETRYCOLLECTIONM:
2824 case GAIA_GEOMETRYCOLLECTIONZM:
2825 if (!parse_multi_geom
2826 (blob, size, little_endian, endian_arch, &is_compr))
2827 goto not_a_valid_geom;
2828 if (is_compr)
2829 return GAIA_COMPRESSED_GEOMETRY_BLOB;
2830 else
2831 return GAIA_GEOMETRY_BLOB;
2832 default:
2833 return GAIA_GEOMETRY_BLOB;
2834 }
2835 }
2836
2837 not_a_valid_geom:
2838 /* testing for TinyPoint */
2839 if (size < 24)
2840 tiny_point = 0;
2841 else
2842 {
2843 if (*(blob + 0) != GAIA_MARK_START)
2844 tiny_point = 0;
2845 if (*(blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN
2846 || *(blob + 1) == GAIA_TINYPOINT_BIG_ENDIAN)
2847 ;
2848 else
2849 tiny_point = 0;
2850 if (*(blob + 6) == GAIA_TINYPOINT_XY
2851 || *(blob + 6) == GAIA_TINYPOINT_XYZ
2852 || *(blob + 6) == GAIA_TINYPOINT_XYM
2853 || *(blob + 6) == GAIA_TINYPOINT_XYZM)
2854 ;
2855 else
2856 tiny_point = 0;
2857 if (*(blob + (size - 1)) != GAIA_MARK_END)
2858 tiny_point = 0;
2859 }
2860 if (tiny_point)
2861 return GAIA_TINYPOINT_BLOB;
2862
2863 #ifdef ENABLE_LIBXML2 /* LIBXML2 enabled: supporting XML documents */
2864
2865 if (gaiaIsValidXmlBlob (blob, size))
2866 return GAIA_XML_BLOB;
2867
2868 #endif /* end LIBXML2: supporting XML documents */
2869
2870 #ifdef ENABLE_GEOPACKAGE /* GEOPACKAGE enabled: supporting GPKG geometries */
2871
2872 if (gaiaIsValidGPB (blob, size))
2873 return GAIA_GPB_BLOB;
2874
2875 #endif /* end GEOPACKAGE: supporting GPKG geometries */
2876
2877 return GAIA_HEX_BLOB;
2878 }
2879
2880 GAIAEXIF_DECLARE int
gaiaGetGpsCoords(const unsigned char * blob,int size,double * longitude,double * latitude)2881 gaiaGetGpsCoords (const unsigned char *blob, int size, double *longitude,
2882 double *latitude)
2883 {
2884 /* returns the ExifGps coords, if they exists */
2885 gaiaExifTagListPtr exif_list;
2886 gaiaExifTagPtr pT;
2887 char lat_ref = '\0';
2888 char long_ref = '\0';
2889 double lat_degs = -DBL_MAX;
2890 double lat_mins = -DBL_MAX;
2891 double lat_secs = -DBL_MAX;
2892 double long_degs = -DBL_MAX;
2893 double long_mins = -DBL_MAX;
2894 double long_secs = -DBL_MAX;
2895 double dblval;
2896 double sign;
2897 int ok;
2898 if (size < 1 || !blob)
2899 return 0;
2900 exif_list = gaiaGetExifTags (blob, size);
2901 if (exif_list)
2902 {
2903 pT = exif_list->First;
2904 while (pT)
2905 {
2906 if (pT->Gps && pT->TagId == 0x01)
2907 {
2908 /* ok, this one is the GPSLatitudeRef tag */
2909 if (pT->Type == 2)
2910 lat_ref = *(pT->StringValue);
2911 }
2912 if (pT->Gps && pT->TagId == 0x03)
2913 {
2914 /* ok, this one is the GPSLongitudeRef tag */
2915 if (pT->Type == 2)
2916 long_ref = *(pT->StringValue);
2917 }
2918 if (pT->Gps && pT->TagId == 0x02)
2919 {
2920 /* ok, this one is the GPSLatitude tag */
2921 if (pT->Type == 5 && pT->Count == 3)
2922 {
2923 dblval = gaiaExifTagGetRationalValue (pT, 0, &ok);
2924 if (ok)
2925 lat_degs = dblval;
2926 dblval = gaiaExifTagGetRationalValue (pT, 1, &ok);
2927 if (ok)
2928 lat_mins = dblval;
2929 dblval = gaiaExifTagGetRationalValue (pT, 2, &ok);
2930 if (ok)
2931 lat_secs = dblval;
2932 }
2933 }
2934 if (pT->Gps && pT->TagId == 0x04)
2935 {
2936 /* ok, this one is the GPSLongitude tag */
2937 if (pT->Type == 5 && pT->Count == 3)
2938 {
2939 dblval = gaiaExifTagGetRationalValue (pT, 0, &ok);
2940 if (ok)
2941 long_degs = dblval;
2942 dblval = gaiaExifTagGetRationalValue (pT, 1, &ok);
2943 if (ok)
2944 long_mins = dblval;
2945 dblval = gaiaExifTagGetRationalValue (pT, 2, &ok);
2946 if (ok)
2947 long_secs = dblval;
2948 }
2949 }
2950 pT = pT->Next;
2951 }
2952 gaiaExifTagsFree (exif_list);
2953 if ((lat_ref == 'N' || lat_ref == 'S' || long_ref == 'E'
2954 || long_ref == 'W') && lat_degs != -DBL_MAX
2955 && lat_mins != -DBL_MAX && lat_secs != -DBL_MAX
2956 && long_degs != -DBL_MAX && long_mins != -DBL_MAX
2957 && long_secs != -DBL_MAX)
2958 {
2959 if (lat_ref == 'S')
2960 sign = -1.0;
2961 else
2962 sign = 1.0;
2963 lat_degs = math_round (lat_degs * 1000000.0);
2964 lat_mins = math_round (lat_mins * 1000000.0);
2965 lat_secs = math_round (lat_secs * 1000000.0);
2966 dblval =
2967 math_round (lat_degs + (lat_mins / 60.0) +
2968 (lat_secs / 3600.0)) * (sign / 1000000.0);
2969 *latitude = dblval;
2970 if (long_ref == 'W')
2971 sign = -1.0;
2972 else
2973 sign = 1.0;
2974 long_degs = math_round (long_degs * 1000000.0);
2975 long_mins = math_round (long_mins * 1000000.0);
2976 long_secs = math_round (long_secs * 1000000.0);
2977 dblval =
2978 math_round (long_degs + (long_mins / 60.0) +
2979 (long_secs / 3600.0)) * (sign / 1000000.0);
2980 *longitude = dblval;
2981 return 1;
2982 }
2983 }
2984 return 0;
2985 }
2986
2987 GAIAEXIF_DECLARE int
gaiaGetGpsLatLong(const unsigned char * blob,int size,char * latlong,int ll_size)2988 gaiaGetGpsLatLong (const unsigned char *blob, int size, char *latlong,
2989 int ll_size)
2990 {
2991 /* returns the ExifGps Latitude and Longitude, if they exists */
2992 gaiaExifTagListPtr exif_list;
2993 gaiaExifTagPtr pT;
2994 char lat_ref = '\0';
2995 char long_ref = '\0';
2996 double lat_degs = -DBL_MAX;
2997 double lat_mins = -DBL_MAX;
2998 double lat_secs = -DBL_MAX;
2999 double long_degs = -DBL_MAX;
3000 double long_mins = -DBL_MAX;
3001 double long_secs = -DBL_MAX;
3002 double dblval;
3003 int ok;
3004 char ll[1024];
3005 int len;
3006 *latlong = '\0';
3007 if (size < 1 || !blob)
3008 return 0;
3009 exif_list = gaiaGetExifTags (blob, size);
3010 if (exif_list)
3011 {
3012 pT = exif_list->First;
3013 while (pT)
3014 {
3015 if (pT->Gps && pT->TagId == 0x01)
3016 {
3017 /* ok, this one is the GPSLatitudeRef tag */
3018 if (pT->Type == 2)
3019 lat_ref = *(pT->StringValue);
3020 }
3021 if (pT->Gps && pT->TagId == 0x03)
3022 {
3023 /* ok, this one is the GPSLongitudeRef tag */
3024 if (pT->Type == 2)
3025 long_ref = *(pT->StringValue);
3026 }
3027 if (pT->Gps && pT->TagId == 0x02)
3028 {
3029 /* ok, this one is the GPSLatitude tag */
3030 if (pT->Type == 5 && pT->Count == 3)
3031 {
3032 dblval = gaiaExifTagGetRationalValue (pT, 0, &ok);
3033 if (ok)
3034 lat_degs = dblval;
3035 dblval = gaiaExifTagGetRationalValue (pT, 1, &ok);
3036 if (ok)
3037 lat_mins = dblval;
3038 dblval = gaiaExifTagGetRationalValue (pT, 2, &ok);
3039 if (ok)
3040 lat_secs = dblval;
3041 }
3042 }
3043 if (pT->Gps && pT->TagId == 0x04)
3044 {
3045 /* ok, this one is the GPSLongitude tag */
3046 if (pT->Type == 5 && pT->Count == 3)
3047 {
3048 dblval = gaiaExifTagGetRationalValue (pT, 0, &ok);
3049 if (ok)
3050 long_degs = dblval;
3051 dblval = gaiaExifTagGetRationalValue (pT, 1, &ok);
3052 if (ok)
3053 long_mins = dblval;
3054 dblval = gaiaExifTagGetRationalValue (pT, 2, &ok);
3055 if (ok)
3056 long_secs = dblval;
3057 }
3058 }
3059 pT = pT->Next;
3060 }
3061 gaiaExifTagsFree (exif_list);
3062 if ((lat_ref == 'N' || lat_ref == 'S' || long_ref == 'E'
3063 || long_ref == 'W') && lat_degs != -DBL_MAX
3064 && lat_mins != -DBL_MAX && lat_secs != -DBL_MAX
3065 && long_degs != -DBL_MAX && long_mins != -DBL_MAX
3066 && long_secs != -DBL_MAX)
3067 {
3068 int long_d = long_degs;
3069 int long_m = long_mins;
3070 int long_s = long_secs;
3071 int lat_d = lat_degs;
3072 int lat_m = lat_mins;
3073 int lat_s = lat_secs;
3074 sprintf (ll, "%02d°%02d′%02d″%c %03d°%02d′%02d″%c",
3075 lat_d, lat_m, lat_s, lat_ref, long_d, long_m, long_s,
3076 long_ref);
3077 len = strlen (ll);
3078 if (len < ll_size)
3079 strcpy (latlong, ll);
3080 else
3081 {
3082 memcpy (latlong, ll, ll_size - 1);
3083 latlong[ll_size] = '\0';
3084 }
3085 return 1;
3086 }
3087 }
3088 return 0;
3089 }
3090