PageSourceSearch

https://www.favicon-generator.org/image-editor/libs/exif.js

js favicon-generator.org collected 2026-10-02 02:14:24 UTC 24,518 bytes, 648 lines download raw bytes

1//Javascript EXIF Reader 0.1.6
2//Copyright (c) 2008 Jacob Seidelin, [email protected], http://blog.nihilogic.dk/
3//Licensed under the MPL License [http://www.nihilogic.dk/licenses/mpl-license.txt]
4
5var EXIF = (function() {
6
7    var debug = false;
8
9    var ExifTags = {
10
11        // version tags
12        0x9000 : "ExifVersion",			// EXIF version
13        0xA000 : "FlashpixVersion",		// Flashpix format version
14
15        // colorspace tags
16        0xA001 : "ColorSpace",			// Color space information tag
17
18        // image configuration
19        0xA002 : "PixelXDimension",		// Valid width of meaningful image
20        0xA003 : "PixelYDimension",		// Valid height of meaningful image
21        0x9101 : "ComponentsConfiguration",	// Information about channels
22        0x9102 : "CompressedBitsPerPixel",	// Compressed bits per pixel
23
24        // user information
25        0x927C : "MakerNote",			// Any desired information written by the manufacturer
26        0x9286 : "UserComment",			// Comments by user
27
28        // related file
29        0xA004 : "RelatedSoundFile",		// Name of related sound file
30
31        // date and time
32        0x9003 : "DateTimeOriginal",		// Date and time when the original image was generated
33        0x9004 : "DateTimeDigitized",		// Date and time when the image was stored digitally
34        0x9290 : "SubsecTime",			// Fractions of seconds for DateTime
35        0x9291 : "SubsecTimeOriginal",		// Fractions of seconds for DateTimeOriginal
36        0x9292 : "SubsecTimeDigitized",		// Fractions of seconds for DateTimeDigitized
37
38        // picture-taking conditions
39        0x829A : "ExposureTime",		// Exposure time (in seconds)
40        0x829D : "FNumber",			// F number
41        0x8822 : "ExposureProgram",		// Exposure program
42        0x8824 : "SpectralSensitivity",		// Spectral sensitivity
43        0x8827 : "ISOSpeedRatings",		// ISO speed rating
44        0x8828 : "OECF",			// Optoelectric conversion factor
45        0x9201 : "ShutterSpeedValue",		// Shutter speed
46        0x9202 : "ApertureValue",		// Lens aperture
47        0x9203 : "BrightnessValue",		// Value of brightness
48        0x9204 : "ExposureBias",		// Exposure bias
49        0x9205 : "MaxApertureValue",		// Smallest F number of lens
50        0x9206 : "SubjectDistance",		// Distance to subject in meters
51        0x9207 : "MeteringMode", 		// Metering mode
52        0x9208 : "LightSource",			// Kind of light source
53        0x9209 : "Flash",			// Flash status
54        0x9214 : "SubjectArea",			// Location and area of main subject
55        0x920A : "FocalLength",			// Focal length of the lens in mm
56        0xA20B : "FlashEnergy",			// Strobe energy in BCPS
57        0xA20C : "SpatialFrequencyResponse",	// 
58        0xA20E : "FocalPlaneXResolution", 	// Number of pixels in width direction per FocalPlaneResolutionUnit
59        0xA20F : "FocalPlaneYResolution", 	// Number of pixels in height direction per FocalPlaneResolutionUnit
60        0xA210 : "FocalPlaneResolutionUnit", 	// Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
61        0xA214 : "SubjectLocation",		// Location of subject in image
62        0xA215 : "ExposureIndex",		// Exposure index selected on camera
63        0xA217 : "SensingMethod", 		// Image sensor type
64        0xA300 : "FileSource", 			// Image source (3 == DSC)
65        0xA301 : "SceneType", 			// Scene type (1 == directly photographed)
66        0xA302 : "CFAPattern",			// Color filter array geometric pattern
67        0xA401 : "CustomRendered",		// Special processing
68        0xA402 : "ExposureMode",		// Exposure mode
69        0xA403 : "WhiteBalance",		// 1 = auto white balance, 2 = manual
70        0xA404 : "DigitalZoomRation",		// Digital zoom ratio
71        0xA405 : "FocalLengthIn35mmFilm",	// Equivalent foacl length assuming 35mm film camera (in mm)
72        0xA406 : "SceneCaptureType",		// Type of scene
73        0xA407 : "GainControl",			// Degree of overall image gain adjustment
74        0xA408 : "Contrast",			// Direction of contrast processing applied by camera
75        0xA409 : "Saturation", 			// Direction of saturation processing applied by camera
76        0xA40A : "Sharpness",			// Direction of sharpness processing applied by camera
77        0xA40B : "DeviceSettingDescription",	// 
78        0xA40C : "SubjectDistanceRange",	// Distance to subject
79
80        // other tags
81        0xA005 : "InteroperabilityIFDPointer",
82        0xA420 : "ImageUniqueID"		// Identifier assigned uniquely to each image
83    };
84
85    var TiffTags = {
86        0x0100 : "ImageWidth",
87        0x0101 : "ImageHeight",
88        0x8769 : "ExifIFDPointer",
89        0x8825 : "GPSInfoIFDPointer",
90        0xA005 : "InteroperabilityIFDPointer",
91        0x0102 : "BitsPerSample",
92        0x0103 : "Compression",
93        0x0106 : "PhotometricInterpretation",
94        0x0112 : "Orientation",
95        0x0115 : "SamplesPerPixel",
96        0x011C : "PlanarConfiguration",
97        0x0212 : "YCbCrSubSampling",
98        0x0213 : "YCbCrPositioning",
99        0x011A : "XResolution",
100        0x011B : "YResolution",
101        0x0128 : "ResolutionUnit",
102        0x0111 : "StripOffsets",
103        0x0116 : "RowsPerStrip",
104        0x0117 : "StripByteCounts",
105        0x0201 : "JPEGInterchangeFormat",
106        0x0202 : "JPEGInterchangeFormatLength",
107        0x012D : "TransferFunction",
108        0x013E : "WhitePoint",
109        0x013F : "PrimaryChromaticities",
110        0x0211 : "YCbCrCoefficients",
111        0x0214 : "ReferenceBlackWhite",
112        0x0132 : "DateTime",
113        0x010E : "ImageDescription",
114        0x010F : "Make",
115        0x0110 : "Model",
116        0x0131 : "Software",
117        0x013B : "Artist",
118        0x8298 : "Copyright"
119    };
120
121    var GPSTags = {
122        0x0000 : "GPSVersionID",
123        0x0001 : "GPSLatitudeRef",
124        0x0002 : "GPSLatitude",
125        0x0003 : "GPSLongitudeRef",
126        0x0004 : "GPSLongitude",
127        0x0005 : "GPSAltitudeRef",
128        0x0006 : "GPSAltitude",
129        0x0007 : "GPSTimeStamp",
130        0x0008 : "GPSSatellites",
131        0x0009 : "GPSStatus",
132        0x000A : "GPSMeasureMode",
133        0x000B : "GPSDOP",
134        0x000C : "GPSSpeedRef",
135        0x000D : "GPSSpeed",
136        0x000E : "GPSTrackRef",
137        0x000F : "GPSTrack",
138        0x0010 : "GPSImgDirectionRef",
139        0x0011 : "GPSImgDirection",
140        0x0012 : "GPSMapDatum",
141        0x0013 : "GPSDestLatitudeRef",
142        0x0014 : "GPSDestLatitude",
143        0x0015 : "GPSDestLongitudeRef",
144        0x0016 : "GPSDestLongitude",
145        0x0017 : "GPSDestBearingRef",
146        0x0018 : "GPSDestBearing",
147        0x0019 : "GPSDestDistanceRef",
148        0x001A : "GPSDestDistance",
149        0x001B : "GPSProcessingMethod",
150        0x001C : "GPSAreaInformation",
151        0x001D : "GPSDateStamp",
152        0x001E : "GPSDifferential"
153    };
154
155    var StringValues = {
156        ExposureProgram : {
157            0 : "Not defined",
158            1 : "Manual",
159            2 : "Normal program",
160            3 : "Aperture priority",
161            4 : "Shutter priority",
162            5 : "Creative program",
163            6 : "Action program",
164            7 : "Portrait mode",
165            8 : "Landscape mode"
166        },
167        MeteringMode : {
168            0 : "Unknown",
169            1 : "Average",
170            2 : "CenterWeightedAverage",
171            3 : "Spot",
172            4 : "MultiSpot",
173            5 : "Pattern",
174            6 : "Partial",
175            255 : "Other"
176        },
177        LightSource : {
178            0 : "Unknown",
179            1 : "Daylight",
180            2 : "Fluorescent",
181            3 : "Tungsten (incandescent light)",
182            4 : "Flash",
183            9 : "Fine weather",
184            10 : "Cloudy weather",
185            11 : "Shade",
186            12 : "Daylight fluorescent (D 5700 - 7100K)",
187            13 : "Day white fluorescent (N 4600 - 5400K)",
188            14 : "Cool white fluorescent (W 3900 - 4500K)",
189            15 : "White fluorescent (WW 3200 - 3700K)",
190            17 : "Standard light A",
191            18 : "Standard light B",
192            19 : "Standard light C",
193            20 : "D55",
194            21 : "D65",
195            22 : "D75",
196            23 : "D50",
197            24 : "ISO studio tungsten",
198            255 : "Other"
199        },
200        Flash : {
201            0x0000 : "Flash did not fire",
202            0x0001 : "Flash fired",
203            0x0005 : "Strobe return light not detected",
204            0x0007 : "Strobe return light detected",
205            0x0009 : "Flash fired, compulsory flash mode",
206            0x000D : "Flash fired, compulsory flash mode, return light not detected",
207            0x000F : "Flash fired, compulsory flash mode, return light detected",
208            0x0010 : "Flash did not fire, compulsory flash mode",
209            0x0018 : "Flash did not fire, auto mode",
210            0x0019 : "Flash fired, auto mode",
211            0x001D : "Flash fired, auto mode, return light not detected",
212            0x001F : "Flash fired, auto mode, return light detected",
213            0x0020 : "No flash function",
214            0x0041 : "Flash fired, red-eye reduction mode",
215            0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
216            0x0047 : "Flash fired, red-eye reduction mode, return light detected",
217            0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
218            0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
219            0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
220            0x0059 : "Flash fired, auto mode, red-eye reduction mode",
221            0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
222            0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
223        },
224        SensingMethod : {
225            1 : "Not defined",
226            2 : "One-chip color area sensor",
227            3 : "Two-chip color area sensor",
228            4 : "Three-chip color area sensor",
229            5 : "Color sequential area sensor",
230            7 : "Trilinear sensor",
231            8 : "Color sequential linear sensor"
232        },
233        SceneCaptureType : {
234            0 : "Standard",
235            1 : "Landscape",
236            2 : "Portrait",
237            3 : "Night scene"
238        },
239        SceneType : {
240            1 : "Directly photographed"
241        },
242        CustomRendered : {
243            0 : "Normal process",
244            1 : "Custom process"
245        },
246        WhiteBalance : {
247            0 : "Auto white balance",
248            1 : "Manual white balance"
249        },
250        GainControl : {
251            0 : "None",
252            1 : "Low gain up",
253            2 : "High gain up",
254            3 : "Low gain down",
255            4 : "High gain down"
256        },
257        Contrast : {
258            0 : "Normal",
259            1 : "Soft",
260            2 : "Hard"
261        },
262        Saturation : {
263            0 : "Normal",
264            1 : "Low saturation",
265            2 : "High saturation"
266        },
267        Sharpness : {
268            0 : "Normal",
269            1 : "Soft",
270            2 : "Hard"
271        },
272        SubjectDistanceRange : {
273            0 : "Unknown",
274            1 : "Macro",
275            2 : "Close view",
276            3 : "Distant view"
277        },
278        FileSource : {
279            3 : "DSC"
280        },
281
282        Components : {
283            0 : "",
284            1 : "Y",
285            2 : "Cb",
286            3 : "Cr",
287            4 : "R",
288            5 : "G",
289            6 : "B"
290        }
291    };
292
293    function addEvent(element, event, handler) {
294        if (element.addEventListener) { 
295            element.addEventListener(event, handler, false); 
296        } else if (element.attachEvent) { 
297            element.attachEvent("on" + event, handler); 
298        }
299    }
300
301    function imageHasData(img) {
302        return !!(img.exifdata);
303    }
304
305    function getImageData(img, callback) {
306        function handleBinaryFile(binFile) {
307            var data = findEXIFinJPEG(binFile);
308            img.exifdata = data || {};
309            if (callback) {
310                callback.call(img)
311            }
312        }
313
314        if (img instanceof Image) {
315            BinaryAjax(img.src, function(http) {
316                handleBinaryFile(http.binaryResponse);
317            });
318        } else if (window.FileReader && img instanceof window.File) {
319            var fileReader = new FileReader();
320
321            fileReader.onload = function(e) {
322                handleBinaryFile(new BinaryFile(e.target.result));
323            };
324
325            try{
326		fileReader.readAsBinaryString(img);
327	    }
328	    catch(err){
329		console.log("exif.js error: "+err.message);
330		}
331	    
332        }
333    }
334
335    function findEXIFinJPEG(file) {
336        if (file.getByteAt(0) != 0xFF || file.getByteAt(1) != 0xD8) {
337            return false; // not a valid jpeg
338        }
339
340        var offset = 2,
341            length = file.getLength(),
342            marker;
343
344        while (offset < length) {
345            if (file.getByteAt(offset) != 0xFF) {
346                if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + file.getByteAt(offset));
347                return false; // not a valid marker, something is wrong
348            }
349
350            marker = file.getByteAt(offset+1);
351
352            // we could implement handling for other markers here, 
353            // but we're only looking for 0xFFE1 for EXIF data
354
355            if (marker == 22400) {
356                if (debug) console.log("Found 0xFFE1 marker");
357                
358                return readEXIFData(file, offset + 4, file.getShortAt(offset+2, true)-2);
359                
360                // offset += 2 + file.getShortAt(offset+2, true);
361
362            } else if (marker == 225) {
363                // 0xE1 = Application-specific 1 (for EXIF)
364                if (debug) console.log("Found 0xFFE1 marker");
365                
366                return readEXIFData(file, offset + 4, file.getShortAt(offset+2, true)-2);
367
368            } else {
369                offset += 2 + file.getShortAt(offset+2, true);
370            }
371
372        }
373
374    }
375
376
377    function readTags(file, tiffStart, dirStart, strings, bigEnd) {
378        var entries = file.getShortAt(dirStart, bigEnd),
379            tags = {}, 
380            entryOffset, tag,
381            i;
382            
383        for (i=0;i<entries;i++) {
384            entryOffset = dirStart + i*12 + 2;
385            tag = strings[file.getShortAt(entryOffset, bigEnd)];
386            if (!tag && debug) console.log("Unknown tag: " + file.getShortAt(entryOffset, bigEnd));
387            tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
388        }
389        return tags;
390    }
391
392
393    function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
394        var type = file.getShortAt(entryOffset+2, bigEnd),
395            numValues = file.getLongAt(entryOffset+4, bigEnd),
396            valueOffset = file.getLongAt(entryOffset+8, bigEnd) + tiffStart,
397            offset,
398            vals, val, n,
399            numerator, denominator;
400
401        switch (type) {
402            case 1: // byte, 8-bit unsigned int
403            case 7: // undefined, 8-bit byte, value depending on field
404                if (numValues == 1) {
405                    return file.getByteAt(entryOffset + 8, bigEnd);
406                } else {
407                    offset = numValues > 4 ? valueOffset : (entryOffset + 8);
408                    vals = [];
409                    for (n=0;n<numValues;n++) {
410                        vals[n] = file.getByteAt(offset + n);
411                    }
412                    return vals;
413                }
414
415            case 2: // ascii, 8-bit byte
416                offset = numValues > 4 ? valueOffset : (entryOffset + 8);
417                return file.getStringAt(offset, numValues-1);
418
419            case 3: // short, 16 bit int
420                if (numValues == 1) {
421                    return file.getShortAt(entryOffset + 8, bigEnd);
422                } else {
423                    offset = numValues > 2 ? valueOffset : (entryOffset + 8);
424                    vals = [];
425                    for (n=0;n<numValues;n++) {
426                        vals[n] = file.getShortAt(offset + 2*n, bigEnd);
427                    }
428                    return vals;
429                }
430
431            case 4: // long, 32 bit int
432                if (numValues == 1) {
433                    return file.getLongAt(entryOffset + 8, bigEnd);
434                } else {
435                    vals = [];
436                    for (var n=0;n<numValues;n++) {
437                        vals[n] = file.getLongAt(valueOffset + 4*n, bigEnd);
438                    }
439                    return vals;
440                }
441
442            case 5:	// rational = two long values, first is numerator, second is denominator
443                if (numValues == 1) {
444                    numerator = file.getLongAt(valueOffset, bigEnd);
445                    denominator = file.getLongAt(valueOffset+4, bigEnd);
446                    val = new Number(numerator / denominator);
447                    val.numerator = numerator;
448                    val.denominator = denominator;
449                    return val;
450                } else {
451                    vals = [];
452                    for (n=0;n<numValues;n++) {
453                        numerator = file.getLongAt(valueOffset + 8*n, bigEnd);
454                        denominator = file.getLongAt(valueOffset+4 + 8*n, bigEnd);
455                        vals[n] = new Number(numerator / denominator);
456                        vals[n].numerator = numerator;
457                        vals[n].denominator = denominator;
458                    }
459                    return vals;
460                }
461
462            case 9: // slong, 32 bit signed int
463                if (numValues == 1) {
464                    return file.getSLongAt(entryOffset + 8, bigEnd);
465                } else {
466                    vals = [];
467                    for (n=0;n<numValues;n++) {
468                        vals[n] = file.getSLongAt(valueOffset + 4*n, bigEnd);
469                    }
470                    return vals;
471                }
472
473            case 10: // signed rational, two slongs, first is numerator, second is denominator
474                if (numValues == 1) {
475                    return file.getSLongAt(valueOffset, bigEnd) / file.getSLongAt(valueOffset+4, bigEnd);
476                } else {
477                    vals = [];
478                    for (n=0;n<numValues;n++) {
479                        vals[n] = file.getSLongAt(valueOffset + 8*n, bigEnd) / file.getSLongAt(valueOffset+4 + 8*n, bigEnd);
480                    }
481                    return vals;
482                }
483        }
484    }
485
486
487    function readEXIFData(file, start) {
488        if (file.getStringAt(start, 4) != "Exif") {
489            if (debug) console.log("Not valid EXIF data! " + file.getStringAt(start, 4));
490            return false;
491        }
492
493        var bigEnd,
494            tags, tag,
495            exifData, gpsData,
496            tiffOffset = start + 6;
497
498        // test for TIFF validity and endianness
499        if (file.getShortAt(tiffOffset) == 0x4949) {
500            bigEnd = false;
501        } else if (file.getShortAt(tiffOffset) == 0x4D4D) {
502            bigEnd = true;
503        } else {
504            if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
505            return false;
506        }
507
508        if (file.getShortAt(tiffOffset+2, bigEnd) != 0x002A) {
509            if (debug) console.log("Not valid TIFF data! (no 0x002A)");
510            return false;
511        }
512
513        if (file.getLongAt(tiffOffset+4, bigEnd) != 0x00000008) {
514            if (debug) console.log("Not valid TIFF data! (First offset not 8)", file.getShortAt(tiffOffset+4, bigEnd));
515            return false;
516        }
517
518        tags = readTags(file, tiffOffset, tiffOffset+8, TiffTags, bigEnd);
519
520        if (tags.ExifIFDPointer) {
521            exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
522            for (tag in exifData) {
523                switch (tag) {
524                    case "LightSource" :
525                    case "Flash" :
526                    case "MeteringMode" :
527                    case "ExposureProgram" :
528                    case "SensingMethod" :
529                    case "SceneCaptureType" :
530                    case "SceneType" :
531                    case "CustomRendered" :
532                    case "WhiteBalance" : 
533                    case "GainControl" : 
534                    case "Contrast" :
535                    case "Saturation" :
536                    case "Sharpness" : 
537                    case "SubjectDistanceRange" :
538                    case "FileSource" :
539                        exifData[tag] = StringValues[tag][exifData[tag]];
540                        break;
541        
542                    case "ExifVersion" :
543                    case "FlashpixVersion" :
544                        exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
545                        break;
546        
547                    case "ComponentsConfiguration" : 
548                        exifData[tag] = 
549                            StringValues.Components[exifData[tag][0]]
550                            + StringValues.Components[exifData[tag][1]]
551                            + StringValues.Components[exifData[tag][2]]
552                            + StringValues.Components[exifData[tag][3]];
553                        break;
554                }
555                tags[tag] = exifData[tag];
556            }
557        }
558
559        if (tags.GPSInfoIFDPointer) {
560            gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
561            for (tag in gpsData) {
562                switch (tag) {
563                    case "GPSVersionID" : 
564                        gpsData[tag] = gpsData[tag][0] 
565                            + "." + gpsData[tag][1] 
566                            + "." + gpsData[tag][2] 
567                            + "." + gpsData[tag][3];
568                        break;
569                }
570                tags[tag] = gpsData[tag];
571            }
572        }
573
574        return tags;
575    }
576
577
578    function getData(img, callback) {
579        if (img instanceof Image && !img.complete) return false;
580        if (!imageHasData(img)) {
581            getImageData(img, callback);
582        } else {
583            if (callback) {
584                callback.call(img);
585            }
586        }
587        return true;
588    }
589
590    function getTag(img, tag) {
591        if (!imageHasData(img)) return;
592        return img.exifdata[tag];
593    }
594
595    function getAllTags(img) {
596        if (!imageHasData(img)) return {};
597        var a, 
598            data = img.exifdata,
599            tags = {};
600        for (a in data) {
601            if (data.hasOwnProperty(a)) {
602                tags[a] = data[a];
603            }
604        }
605        return tags;
606    }
607
608    function pretty(img) {
609        if (!imageHasData(img)) return "";
610        var a,
611            data = img.exifdata,
612            strPretty = "";
613        for (a in data) {
614            if (data.hasOwnProperty(a)) {
615                if (typeof data[a] == "object") {
616                    if (data[a] instanceof Number) {
617                        strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
618                    } else {
619                        strPretty += a + " : [" + data[a].length + " values]\r\n";
620                    }
621                } else {
622                    strPretty += a + " : " + data[a] + "\r\n";
623                }
624            }
625        }
626        return strPretty;
627    }
628
629    function readFromBinaryFile(file) {
630        return findEXIFinJPEG(file);
631    }
632
633   
634    return {
635        readFromBinaryFile : readFromBinaryFile,
636        pretty : pretty,
637        getTag : getTag,
638        getAllTags : getAllTags,
639        getData : getData,
640        
641        Tags : ExifTags,
642        TiffTags : TiffTags,
643        GPSTags : GPSTags,
644        StringValues : StringValues
645    };
646
647})();
648

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.