Localisation updates from https://translatewiki.net.
[mediawiki.git] / resources / src / mediawiki.libs.jpegmeta / jpegmeta.js
blobed859142ccd3c8b6d8fc7f7f99005869e48064b8
1 /**
2  * This is JsJpegMeta v1.0
3  * From: https://code.google.com/p/jsjpegmeta/downloads/list
4  * From: https://github.com/bennoleslie/jsjpegmeta/blob/v1.0.0/jpegmeta.js
5  *
6  * Ported to MediaWiki ResourceLoader by Bryan Tong Minh
7  * Changes:
8  * - Add closure.
9  * - Add this.JpegMeta assignment to expose it as global.
10  */
12 ( function () {
13         /*
14         Copyright (c) 2009 Ben Leslie
15         
16         Permission is hereby granted, free of charge, to any person obtaining a copy
17         of this software and associated documentation files (the "Software"), to deal
18         in the Software without restriction, including without limitation the rights
19         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20         copies of the Software, and to permit persons to whom the Software is
21         furnished to do so, subject to the following conditions:
22         
23         The above copyright notice and this permission notice shall be included in
24         all copies or substantial portions of the Software.
25         
26         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
32         THE SOFTWARE.
33         */
34         
35         /*
36          This JavaScript library is used to parse meta-data from files 
37          with mime-type image/jpeg.
38         
39          Include it with something like:
40         
41            <script type="text/javascript" src="jpegmeta.js"></script>
42         
43          This adds a single 'module' object called 'JpegMeta' to the global
44          namespace.
45         
46          Public Functions
47          ----------------
48          JpegMeta.parseNum - parse unsigned integers from binary data
49          JpegMeta.parseSnum - parse signed integers from binary data
50         
51          Public Classes
52          --------------
53          JpegMeta.Rational - A rational number class
54          JpegMeta.JfifSegment
55          JpegMeta.ExifSegment
56          JpegMeta.JpegFile - Primary class for Javascript parsing
57         */
59         var JpegMeta = {};
60         // MediaWiki: Expose as global
61         this.JpegMeta = JpegMeta;
62         
63         /* 
64            parse an unsigned number of size bytes at offset in some binary string data.
65            If endian
66            is "<" parse the data as little endian, if endian
67            is ">" parse as big-endian.
68         */
69         JpegMeta.parseNum = function parseNum(endian, data, offset, size) {
70             var i;
71             var ret;
72             var big_endian = (endian === ">");
73             if (offset === undefined) offset = 0;
74             if (size === undefined) size = data.length - offset;
75             for (big_endian ? i = offset : i = offset + size - 1; 
76                  big_endian ? i < offset + size : i >= offset; 
77                  big_endian ? i++ : i--) {
78                 ret <<= 8;
79                 ret += data.charCodeAt(i);
80             }
81             return ret;
82         };
83         
84         /* 
85            parse an signed number of size bytes at offset in some binary string data.
86            If endian
87            is "<" parse the data as little endian, if endian
88            is ">" parse as big-endian.
89         */
90         JpegMeta.parseSnum = function parseSnum(endian, data, offset, size) {
91             var i;
92             var ret;
93             var neg;
94             var big_endian = (endian === ">");
95             if (offset === undefined) offset = 0;
96             if (size === undefined) size = data.length - offset;
97             for (big_endian ? i = offset : i = offset + size - 1; 
98                  big_endian ? i < offset + size : i >= offset; 
99                  big_endian ? i++ : i--) {
100                 if (neg === undefined) {
101                     /* Negative if top bit is set */
102                     neg = (data.charCodeAt(i) & 0x80) === 0x80;
103                 }
104                 ret <<= 8;
105                 /* If it is negative we invert the bits */
106                 ret += neg ? ~data.charCodeAt(i) & 0xff: data.charCodeAt(i);
107             }
108             if (neg) {
109                 /* If it is negative we do two's complement */
110                 ret += 1;
111                 ret *= -1;
112             }
113             return ret;
114         };
115         
116         /* Rational number class */
117         JpegMeta.Rational = function Rational(num, den)
118         {
119             this.num = num;
120             this.den = den || 1;
121             return this;
122         };
123         
124         /* Rational number methods */
125         JpegMeta.Rational.prototype.toString = function toString() {
126             if (this.num === 0) {
127                 return "" + this.num;
128             }
129             if (this.den === 1) {
130                 return "" + this.num;
131             }
132             if (this.num === 1) {
133                 return this.num + " / " + this.den;
134             }
135             return this.num / this.den; // + "/" + this.den;
136         };
137         
138         JpegMeta.Rational.prototype.asFloat = function asFloat() {
139             return this.num / this.den;
140         };
141         
142         /* MetaGroup class */
143         JpegMeta.MetaGroup = function MetaGroup(fieldName, description) {
144             this.fieldName = fieldName;
145             this.description = description;
146             this.metaProps = {};
147             return this;
148         };
149         
150         JpegMeta.MetaGroup.prototype._addProperty = function _addProperty(fieldName, description, value) {
151             var property = new JpegMeta.MetaProp(fieldName, description, value);
152             this[property.fieldName] = property;
153             this.metaProps[property.fieldName] = property;
154         };
155         
156         JpegMeta.MetaGroup.prototype.toString = function toString() {
157             return "[MetaGroup " + this.description + "]";
158         };
160         /* MetaProp class */
161         JpegMeta.MetaProp = function MetaProp(fieldName, description, value) {
162             this.fieldName = fieldName;
163             this.description = description;
164             this.value = value;
165             return this;
166         };
167         
168         JpegMeta.MetaProp.prototype.toString = function toString() {
169             return "" + this.value;
170         };
172         /* JpegFile class */
173         JpegMeta.JpegFile = function JpegFile(binary_data, filename) {
174             /* Change this to EOI if we want to parse. */
175             var break_segment = this._SOS;
176             
177             this.metaGroups = {};
178             this._binary_data = binary_data;
179             this.filename = filename;
180             
181             /* Go through and parse. */
182             var pos = 0;
183             var pos_start_of_segment = 0;
184             var delim;
185             var mark;
186             var _mark;
187             var segsize;
188             var headersize;
189             var mark_code;
190             var mark_fn;
191         
192             /* Check to see if this looks like a JPEG file */
193             if (this._binary_data.slice(0, 2) !== this._SOI_MARKER) {
194                 throw new Error("Doesn't look like a JPEG file. First two bytes are " + 
195                                 this._binary_data.charCodeAt(0) + "," + 
196                                 this._binary_data.charCodeAt(1) + ".");
197             }
198             
199             pos += 2;
200             
201             while (pos < this._binary_data.length) {
202                 delim = this._binary_data.charCodeAt(pos++);
203                 mark = this._binary_data.charCodeAt(pos++);
204                 
205                 pos_start_of_segment = pos;
206                 
207                 if (delim != this._DELIM) {
208                     break;
209                 }
210                 
211                 if (mark === break_segment) {
212                     break;
213                 }
214                 
215                 headersize = JpegMeta.parseNum(">", this._binary_data, pos, 2);
216                 
217                 /* Find the end */
218                 pos += headersize;
219                 while (pos < this._binary_data.length) {
220                     delim = this._binary_data.charCodeAt(pos++);
221                     if (delim == this._DELIM) {
222                         _mark = this._binary_data.charCodeAt(pos++);
223                         if (_mark != 0x0) {
224                             pos -= 2;
225                             break;
226                         }
227                     }
228                 }
229                 
230                 segsize = pos - pos_start_of_segment;
231                 
232                 if (this._markers[mark]) {
233                     mark_code = this._markers[mark][0];
234                     mark_fn = this._markers[mark][1];
235                 } else {
236                     mark_code = "UNKN";
237                     mark_fn = undefined;
238                 }
239                 
240                 if (mark_fn) {
241                     this[mark_fn](mark, pos_start_of_segment + 2);
242                 }
243                 
244             }
245             
246             if (this.general === undefined) {
247                 throw Error("Invalid JPEG file.");
248             }
249             
250             return this;
251         };
252         
253         this.JpegMeta.JpegFile.prototype.toString = function () {
254             return "[JpegFile " + this.filename + " " + 
255                 this.general.type + " " + 
256                 this.general.pixelWidth + "x" + 
257                 this.general.pixelHeight +
258                 " Depth: " + this.general.depth + "]";
259         };
260         
261         /* Some useful constants */
262         this.JpegMeta.JpegFile.prototype._SOI_MARKER = '\xff\xd8';
263         this.JpegMeta.JpegFile.prototype._DELIM = 0xff;
264         this.JpegMeta.JpegFile.prototype._EOI = 0xd9;
265         this.JpegMeta.JpegFile.prototype._SOS = 0xda;
266         
267         this.JpegMeta.JpegFile.prototype._sofHandler = function _sofHandler (mark, pos) {
268             if (this.general !== undefined) {
269                 throw Error("Unexpected multiple-frame image");
270             }
271         
272             this._addMetaGroup("general", "General");
273             this.general._addProperty("depth", "Depth", JpegMeta.parseNum(">", this._binary_data, pos, 1));
274             this.general._addProperty("pixelHeight", "Pixel Height", JpegMeta.parseNum(">", this._binary_data, pos + 1, 2));
275             this.general._addProperty("pixelWidth", "Pixel Width",JpegMeta.parseNum(">", this._binary_data, pos + 3, 2));
276             this.general._addProperty("type", "Type", this._markers[mark][2]);
277         };
278         
279         /* JFIF idents */
280         this.JpegMeta.JpegFile.prototype._JFIF_IDENT = "JFIF\x00";
281         this.JpegMeta.JpegFile.prototype._JFXX_IDENT = "JFXX\x00";
282         
283         /* Exif idents */
284         this.JpegMeta.JpegFile.prototype._EXIF_IDENT = "Exif\x00";
285         
286         /* TIFF types */
287         this.JpegMeta.JpegFile.prototype._types = {
288             /* The format is identifier : ["type name", type_size_in_bytes ] */
289             1 : ["BYTE", 1],
290             2 : ["ASCII", 1],
291             3 : ["SHORT", 2],
292             4 : ["LONG", 4],
293             5 : ["RATIONAL", 8],
294             6 : ["SBYTE", 1],
295             7 : ["UNDEFINED", 1],
296             8 : ["SSHORT", 2],
297             9 : ["SLONG", 4],
298             10 : ["SRATIONAL", 8],
299             11 : ["FLOAT", 4],
300             12 : ["DOUBLE", 8]
301         };
302         
303         this.JpegMeta.JpegFile.prototype._tifftags = {
304             /* A. Tags relating to image data structure */
305             256 : ["Image width", "ImageWidth"],
306             257 : ["Image height", "ImageLength"],
307             258 : ["Number of bits per component", "BitsPerSample"],
308             259 : ["Compression scheme", "Compression", 
309                    {1 : "uncompressed", 6 : "JPEG compression" }],
310             262 : ["Pixel composition", "PhotmetricInerpretation",
311                    {2 : "RGB", 6 : "YCbCr"}],
312             274 : ["Orientation of image", "Orientation",
313                    /* FIXME: Check the mirror-image / reverse encoding and rotation */
314                    {1 : "Normal", 2 : "Reverse?", 
315                     3 : "Upside-down", 4 : "Upside-down Reverse",
316                     5 : "90 degree CW", 6 : "90 degree CW reverse",
317                     7 : "90 degree CCW", 8 : "90 degree CCW reverse"}],
318             277 : ["Number of components", "SamplesPerPixel"],
319             284 : ["Image data arrangement", "PlanarConfiguration",
320                    {1 : "chunky format", 2 : "planar format"}],
321             530 : ["Subsampling ratio of Y to C", "YCbCrSubSampling"],
322             531 : ["Y and C positioning", "YCbCrPositioning",
323                    {1 : "centered", 2 : "co-sited"}],
324             282 : ["X Resolution", "XResolution"],
325             283 : ["Y Resolution", "YResolution"],
326             296 : ["Resolution Unit", "ResolutionUnit",
327                    {2 : "inches", 3 : "centimeters"}],
328             /* B. Tags realting to recording offset */
329             273 : ["Image data location", "StripOffsets"],
330             278 : ["Number of rows per strip", "RowsPerStrip"],
331             279 : ["Bytes per compressed strip", "StripByteCounts"],
332             513 : ["Offset to JPEG SOI", "JPEGInterchangeFormat"],
333             514 : ["Bytes of JPEG Data", "JPEGInterchangeFormatLength"],
334             /* C. Tags relating to image data characteristics */
335             301 : ["Transfer function", "TransferFunction"],
336             318 : ["White point chromaticity", "WhitePoint"],
337             319 : ["Chromaticities of primaries", "PrimaryChromaticities"],
338             529 : ["Color space transformation matrix coefficients", "YCbCrCoefficients"],
339             532 : ["Pair of black and white reference values", "ReferenceBlackWhite"],
340             /* D. Other tags */
341             306 : ["Date and time", "DateTime"],
342             270 : ["Image title", "ImageDescription"],
343             271 : ["Make", "Make"],
344             272 : ["Model", "Model"],
345             305 : ["Software", "Software"],
346             315 : ["Person who created the image", "Artist"],
347             316 : ["Host Computer", "HostComputer"],
348             33432 : ["Copyright holder", "Copyright"],
349             
350             34665 : ["Exif tag", "ExifIfdPointer"],
351             34853 : ["GPS tag", "GPSInfoIfdPointer"]
352         };
353         
354         this.JpegMeta.JpegFile.prototype._exiftags = {
355             /* Tag Support Levels (2) - 0th IFX Exif Private Tags */
356             /* A. Tags Relating to Version */
357             36864 : ["Exif Version", "ExifVersion"],
358             40960 : ["FlashPix Version", "FlashpixVersion"],
359             
360             /* B. Tag Relating to Image Data Characteristics */
361             40961 : ["Color Space", "ColorSpace"],
362             
363             /* C. Tags Relating to Image Configuration */
364             37121 : ["Meaning of each component", "ComponentsConfiguration"],
365             37122 : ["Compressed Bits Per Pixel", "CompressedBitsPerPixel"],
366             40962 : ["Pixel X Dimension", "PixelXDimension"],
367             40963 : ["Pixel Y Dimension", "PixelYDimension"],
368             
369             /* D. Tags Relating to User Information */
370             37500 : ["Manufacturer notes", "MakerNote"],
371             37510 : ["User comments", "UserComment"],
372             
373             /* E. Tag Relating to Related File Information */
374             40964 : ["Related audio file", "RelatedSoundFile"],
375             
376             /* F. Tags Relating to Date and Time */
377             36867 : ["Date Time Original", "DateTimeOriginal"],
378             36868 : ["Date Time Digitized", "DateTimeDigitized"],
379             37520 : ["DateTime subseconds", "SubSecTime"],
380             37521 : ["DateTimeOriginal subseconds", "SubSecTimeOriginal"],
381             37522 : ["DateTimeDigitized subseconds", "SubSecTimeDigitized"],
382             
383             /* G. Tags Relating to Picture-Taking Conditions */
384             33434 : ["Exposure time", "ExposureTime"],
385             33437 : ["FNumber", "FNumber"],
386             34850 : ["Exposure program", "ExposureProgram"],
387             34852 : ["Spectral sensitivity", "SpectralSensitivity"],
388             34855 : ["ISO Speed Ratings", "ISOSpeedRatings"],
389             34856 : ["Optoelectric coefficient", "OECF"],
390             37377 : ["Shutter Speed",  "ShutterSpeedValue"],
391             37378 : ["Aperture Value", "ApertureValue"],
392             37379 : ["Brightness", "BrightnessValue"],
393             37380 : ["Exposure Bias Value", "ExposureBiasValue"],
394             37381 : ["Max Aperture Value", "MaxApertureValue"],
395             37382 : ["Subject Distance", "SubjectDistance"],
396             37383 : ["Metering Mode", "MeteringMode"],
397             37384 : ["Light Source", "LightSource"],
398             37385 : ["Flash", "Flash"],
399             37386 : ["Focal Length", "FocalLength"],
400             37396 : ["Subject Area", "SubjectArea"],
401             41483 : ["Flash Energy", "FlashEnergy"],
402             41484 : ["Spatial Frequency Response", "SpatialFrequencyResponse"],
403             41486 : ["Focal Plane X Resolution", "FocalPlaneXResolution"],
404             41487 : ["Focal Plane Y Resolution", "FocalPlaneYResolution"],
405             41488 : ["Focal Plane Resolution Unit", "FocalPlaneResolutionUnit"],
406             41492 : ["Subject Location", "SubjectLocation"],
407             41493 : ["Exposure Index", "ExposureIndex"],
408             41495 : ["Sensing Method", "SensingMethod"],
409             41728 : ["File Source", "FileSource"],
410             41729 : ["Scene Type", "SceneType"],
411             41730 : ["CFA Pattern", "CFAPattern"],
412             41985 : ["Custom Rendered", "CustomRendered"],
413             41986 : ["Exposure Mode", "Exposure Mode"],
414             41987 : ["White Balance", "WhiteBalance"],
415             41988 : ["Digital Zoom Ratio", "DigitalZoomRatio"],
416             41990 : ["Scene Capture Type", "SceneCaptureType"],
417             41991 : ["Gain Control", "GainControl"],
418             41992 : ["Contrast", "Contrast"],
419             41993 : ["Saturation", "Saturation"],
420             41994 : ["Sharpness", "Sharpness"],
421             41995 : ["Device settings description", "DeviceSettingDescription"],
422             41996 : ["Subject distance range", "SubjectDistanceRange"],
423             
424             /* H. Other Tags */
425             42016 : ["Unique image ID", "ImageUniqueID"],
426             
427             40965 : ["Interoperability tag", "InteroperabilityIFDPointer"]
428         };
429         
430         this.JpegMeta.JpegFile.prototype._gpstags = {
431             /* A. Tags Relating to GPS */
432             0 : ["GPS tag version", "GPSVersionID"],
433             1 : ["North or South Latitude", "GPSLatitudeRef"],
434             2 : ["Latitude", "GPSLatitude"],
435             3 : ["East or West Longitude", "GPSLongitudeRef"],
436             4 : ["Longitude", "GPSLongitude"],
437             5 : ["Altitude reference", "GPSAltitudeRef"],
438             6 : ["Altitude", "GPSAltitude"],
439             7 : ["GPS time (atomic clock)", "GPSTimeStamp"],
440             8 : ["GPS satellites usedd for measurement", "GPSSatellites"],
441             9 : ["GPS receiver status", "GPSStatus"],
442             10 : ["GPS mesaurement mode", "GPSMeasureMode"],
443             11 : ["Measurement precision", "GPSDOP"],
444             12 : ["Speed unit", "GPSSpeedRef"],
445             13 : ["Speed of GPS receiver", "GPSSpeed"],
446             14 : ["Reference for direction of movement", "GPSTrackRef"],
447             15 : ["Direction of movement", "GPSTrack"],
448             16 : ["Reference for direction of image", "GPSImgDirectionRef"],
449             17 : ["Direction of image", "GPSImgDirection"],
450             18 : ["Geodetic survey data used", "GPSMapDatum"],
451             19 : ["Reference for latitude of destination", "GPSDestLatitudeRef"],
452             20 : ["Latitude of destination", "GPSDestLatitude"],
453             21 : ["Reference for longitude of destination", "GPSDestLongitudeRef"],
454             22 : ["Longitude of destination", "GPSDestLongitude"],
455             23 : ["Reference for bearing of destination", "GPSDestBearingRef"],
456             24 : ["Bearing of destination", "GPSDestBearing"],
457             25 : ["Reference for distance to destination", "GPSDestDistanceRef"],
458             26 : ["Distance to destination", "GPSDestDistance"],
459             27 : ["Name of GPS processing method", "GPSProcessingMethod"],
460             28 : ["Name of GPS area", "GPSAreaInformation"],
461             29 : ["GPS Date", "GPSDateStamp"],
462             30 : ["GPS differential correction", "GPSDifferential"]
463         };
465         this.JpegMeta.JpegFile.prototype._markers = {
466             /* Start Of Frame markers, non-differential, Huffman coding */
467             0xc0: ["SOF0", "_sofHandler", "Baseline DCT"],
468             0xc1: ["SOF1", "_sofHandler", "Extended sequential DCT"],
469             0xc2: ["SOF2", "_sofHandler", "Progressive DCT"],
470             0xc3: ["SOF3", "_sofHandler", "Lossless (sequential)"],
471             
472             /* Start Of Frame markers, differential, Huffman coding */
473             0xc5: ["SOF5", "_sofHandler", "Differential sequential DCT"],
474             0xc6: ["SOF6", "_sofHandler", "Differential progressive DCT"],
475             0xc7: ["SOF7", "_sofHandler", "Differential lossless (sequential)"],
476             
477             /* Start Of Frame markers, non-differential, arithmetic coding */
478             0xc8: ["JPG", null, "Reserved for JPEG extensions"],
479             0xc9: ["SOF9", "_sofHandler", "Extended sequential DCT"],
480             0xca: ["SOF10", "_sofHandler", "Progressive DCT"],
481             0xcb: ["SOF11", "_sofHandler", "Lossless (sequential)"],
482             
483             /* Start Of Frame markers, differential, arithmetic coding */
484             0xcd: ["SOF13", "_sofHandler", "Differential sequential DCT"],
485             0xce: ["SOF14", "_sofHandler", "Differential progressive DCT"],
486             0xcf: ["SOF15", "_sofHandler", "Differential lossless (sequential)"],
487             
488             /* Huffman table specification */
489             0xc4: ["DHT", null, "Define Huffman table(s)"],
490             0xcc: ["DAC", null, "Define arithmetic coding conditioning(s)"],
491             
492             /* Restart interval termination" */
493             0xd0: ["RST0", null, "Restart with modulo 8 count “0”"],
494             0xd1: ["RST1", null, "Restart with modulo 8 count “1”"],
495             0xd2: ["RST2", null, "Restart with modulo 8 count “2”"],
496             0xd3: ["RST3", null, "Restart with modulo 8 count “3”"],
497             0xd4: ["RST4", null, "Restart with modulo 8 count “4”"],
498             0xd5: ["RST5", null, "Restart with modulo 8 count “5”"],
499             0xd6: ["RST6", null, "Restart with modulo 8 count “6”"],
500             0xd7: ["RST7", null, "Restart with modulo 8 count “7”"],
501             
502             /* Other markers */
503             0xd8: ["SOI", null, "Start of image"],
504             0xd9: ["EOI", null, "End of image"],
505             0xda: ["SOS", null, "Start of scan"],
506             0xdb: ["DQT", null, "Define quantization table(s)"],
507             0xdc: ["DNL", null, "Define number of lines"],
508             0xdd: ["DRI", null, "Define restart interval"],
509             0xde: ["DHP", null, "Define hierarchical progression"],
510             0xdf: ["EXP", null, "Expand reference component(s)"],
511             0xe0: ["APP0", "_app0Handler", "Reserved for application segments"],
512             0xe1: ["APP1", "_app1Handler"],
513             0xe2: ["APP2", null],
514             0xe3: ["APP3", null],
515             0xe4: ["APP4", null],
516             0xe5: ["APP5", null],
517             0xe6: ["APP6", null],
518             0xe7: ["APP7", null],
519             0xe8: ["APP8", null],
520             0xe9: ["APP9", null],
521             0xea: ["APP10", null],
522             0xeb: ["APP11", null],
523             0xec: ["APP12", null],
524             0xed: ["APP13", null],
525             0xee: ["APP14", null],
526             0xef: ["APP15", null],
527             0xf0: ["JPG0", null], /* Reserved for JPEG extensions */
528             0xf1: ["JPG1", null],
529             0xf2: ["JPG2", null],
530             0xf3: ["JPG3", null],
531             0xf4: ["JPG4", null],
532             0xf5: ["JPG5", null],
533             0xf6: ["JPG6", null],
534             0xf7: ["JPG7", null],
535             0xf8: ["JPG8", null],
536             0xf9: ["JPG9", null],
537             0xfa: ["JPG10", null],
538             0xfb: ["JPG11", null],
539             0xfc: ["JPG12", null],
540             0xfd: ["JPG13", null],
541             0xfe: ["COM", null], /* Comment */
542             
543             /* Reserved markers */
544             0x01: ["JPG13", null] /* For temporary private use in arithmetic coding */
545             /* 02 -> bf are reserverd */
546         };
548         /* Private methods */
549         this.JpegMeta.JpegFile.prototype._addMetaGroup = function _addMetaGroup(name, description) {
550             var group = new JpegMeta.MetaGroup(name, description);
551             this[group.fieldName] = group;
552             this.metaGroups[group.fieldName] = group;
553             return group;
554         };
556         this.JpegMeta.JpegFile.prototype._parseIfd = function _parseIfd(endian, _binary_data, base, ifd_offset, tags, name, description) {
557             var num_fields = JpegMeta.parseNum(endian, _binary_data, base + ifd_offset, 2);
558             /* Per tag variables */
559             var i, j;
560             var tag_base;
561             var tag_field;
562             var type, type_field, type_size;
563             var num_values;
564             var value_offset;
565             var value;
566             var _val;
567             var num;
568             var den;
569             
570             var group;
571             
572             group = this._addMetaGroup(name, description);
573         
574             for (var i = 0; i < num_fields; i++) {
575                 /* parse the field */
576                 tag_base = base + ifd_offset + 2 + (i * 12);
577                 tag_field = JpegMeta.parseNum(endian, _binary_data, tag_base, 2);
578                 type_field = JpegMeta.parseNum(endian, _binary_data, tag_base + 2, 2);
579                 num_values = JpegMeta.parseNum(endian, _binary_data, tag_base + 4, 4);
580                 value_offset = JpegMeta.parseNum(endian, _binary_data, tag_base + 8, 4);
581                 if (this._types[type_field] === undefined) {
582                     continue;
583                 }
584                 type = this._types[type_field][0];
585                 type_size = this._types[type_field][1];
586                 
587                 if (type_size * num_values <= 4) {
588                     /* Data is in-line */
589                     value_offset = tag_base + 8;
590                 } else {
591                     value_offset = base + value_offset;
592                 }
593                 
594                 /* Read the value */
595                 if (type == "UNDEFINED") {
596                     value = _binary_data.slice(value_offset, value_offset + num_values);
597                 } else if (type == "ASCII") {
598                     value = _binary_data.slice(value_offset, value_offset + num_values);
599                     value = value.split('\x00')[0];
600                     /* strip trail nul */
601                 } else {
602                     value = new Array();
603                     for (j = 0; j < num_values; j++, value_offset += type_size) {
604                         if (type == "BYTE" || type == "SHORT" || type == "LONG") {
605                             value.push(JpegMeta.parseNum(endian, _binary_data, value_offset, type_size));
606                         }
607                         if (type == "SBYTE" || type == "SSHORT" || type == "SLONG") {
608                             value.push(JpegMeta.parseSnum(endian, _binary_data, value_offset, type_size));
609                         }
610                         if (type == "RATIONAL") {
611                             num = JpegMeta.parseNum(endian, _binary_data, value_offset, 4);
612                             den = JpegMeta.parseNum(endian, _binary_data, value_offset + 4, 4);
613                             value.push(new JpegMeta.Rational(num, den));
614                         }
615                         if (type == "SRATIONAL") {
616                             num = JpegMeta.parseSnum(endian, _binary_data, value_offset, 4);
617                             den = JpegMeta.parseSnum(endian, _binary_data, value_offset + 4, 4);
618                             value.push(new JpegMeta.Rational(num, den));
619                         }
620                         value.push();
621                     }
622                     if (num_values === 1) {
623                         value = value[0];
624                     }
625                 }
626                 if (tags[tag_field] !== undefined) {
627                         group._addProperty(tags[tag_field][1], tags[tag_field][0], value);
628                 }
629             }
630         };
632         this.JpegMeta.JpegFile.prototype._jfifHandler = function _jfifHandler(mark, pos) {
633             if (this.jfif !== undefined) {
634                 throw Error("Multiple JFIF segments found");
635             }
636             this._addMetaGroup("jfif", "JFIF");
637             this.jfif._addProperty("version_major", "Version Major", this._binary_data.charCodeAt(pos + 5));
638             this.jfif._addProperty("version_minor", "Version Minor", this._binary_data.charCodeAt(pos + 6));
639             this.jfif._addProperty("version", "JFIF Version", this.jfif.version_major.value + "." + this.jfif.version_minor.value);
640             this.jfif._addProperty("units", "Density Unit", this._binary_data.charCodeAt(pos + 7));
641             this.jfif._addProperty("Xdensity", "X density", JpegMeta.parseNum(">", this._binary_data, pos + 8, 2));
642             this.jfif._addProperty("Ydensity", "Y Density", JpegMeta.parseNum(">", this._binary_data, pos + 10, 2));
643             this.jfif._addProperty("Xthumbnail", "X Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 12, 1));
644             this.jfif._addProperty("Ythumbnail", "Y Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 13, 1));
645         };
647         /* Handle app0 segments */
648         this.JpegMeta.JpegFile.prototype._app0Handler = function app0Handler(mark, pos) {
649             var ident = this._binary_data.slice(pos, pos + 5);
650             if (ident == this._JFIF_IDENT) {
651                 this._jfifHandler(mark, pos);
652             } else if (ident == this._JFXX_IDENT) {
653                 /* Don't handle JFXX Ident yet */
654             } else {
655                 /* Don't know about other idents */
656             }
657         };
659         /* Handle app1 segments */
660         this.JpegMeta.JpegFile.prototype._app1Handler = function _app1Handler(mark, pos) {
661             var ident = this._binary_data.slice(pos, pos + 5);
662             if (ident == this._EXIF_IDENT) {
663                 this._exifHandler(mark, pos + 6);
664             } else {
665                 /* Don't know about other idents */
666             }
667         };
669         /* Handle exif segments */
670         JpegMeta.JpegFile.prototype._exifHandler = function _exifHandler(mark, pos) {
671             if (this.exif !== undefined) {
672                 throw new Error("Multiple JFIF segments found");
673             }
674             
675             /* Parse this TIFF header */
676             var endian;
677             var magic_field;
678             var ifd_offset;
679             var primary_ifd, exif_ifd, gps_ifd;
680             var endian_field = this._binary_data.slice(pos, pos + 2);
681             
682             /* Trivia: This 'I' is for Intel, the 'M' is for Motorola */
683             if (endian_field === "II") {
684                 endian = "<";
685             } else if (endian_field === "MM") {
686                 endian = ">";
687             } else {
688                 throw new Error("Malformed TIFF meta-data. Unknown endianess: " + endian_field);
689             }
690             
691             magic_field = JpegMeta.parseNum(endian, this._binary_data, pos + 2, 2);
692             
693             if (magic_field !== 42) {
694                 throw new Error("Malformed TIFF meta-data. Bad magic: " + magic_field);
695             }
696             
697             ifd_offset = JpegMeta.parseNum(endian, this._binary_data, pos + 4, 4);
698             
699             /* Parse 0th IFD */
700             this._parseIfd(endian, this._binary_data, pos, ifd_offset, this._tifftags, "tiff", "TIFF");
701             
702             if (this.tiff.ExifIfdPointer) {
703                 this._parseIfd(endian, this._binary_data, pos, this.tiff.ExifIfdPointer.value, this._exiftags, "exif", "Exif");
704             }
705             
706             if (this.tiff.GPSInfoIfdPointer) {
707                 this._parseIfd(endian, this._binary_data, pos, this.tiff.GPSInfoIfdPointer.value, this._gpstags, "gps", "GPS");
708                 if (this.gps.GPSLatitude) {
709                     var latitude;
710                     latitude = this.gps.GPSLatitude.value[0].asFloat() + 
711                         (1 / 60) * this.gps.GPSLatitude.value[1].asFloat() + 
712                         (1 / 3600) * this.gps.GPSLatitude.value[2].asFloat();
713                     if (this.gps.GPSLatitudeRef.value === "S") {
714                         latitude = -latitude;
715                     }
716                     this.gps._addProperty("latitude", "Dec. Latitude", latitude);
717                 }
718                 if (this.gps.GPSLongitude) {
719                     var longitude;
720                     longitude = this.gps.GPSLongitude.value[0].asFloat() + 
721                         (1 / 60) * this.gps.GPSLongitude.value[1].asFloat() + 
722                         (1 / 3600) * this.gps.GPSLongitude.value[2].asFloat();
723                     if (this.gps.GPSLongitudeRef.value === "W") {
724                         longitude = -longitude;
725                     }
726                     this.gps._addProperty("longitude", "Dec. Longitude", longitude);
727                 }
728             }
729         };
731 }() );