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