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