SECURITY: resources: Patch jQuery 3.3.1 for CVE-2019-11358
[lhc/web/wiklou.git] / resources / src / mediawiki.libs.jpegmeta / jpegmeta.js
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 */
11
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 */
58
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 };
159
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 };
171
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 };
464
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 };
547
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 };
555
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 };
631
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 };
646
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 };
658
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 };
668
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 };
730
731 }() );