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 */
6 /* JsJpegMeta starts here */
9 Copyright (c) 2009 Ben Leslie
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:
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
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
31 This JavaScript library is used to parse meta-data from files
32 with mime-type image/jpeg.
34 Include it with something like:
36 <script type="text/javascript" src="jpegmeta.js"></script>
38 This adds a single 'module' object called 'JpegMeta' to the global
43 JpegMeta.parseNum - parse unsigned integers from binary data
44 JpegMeta.parseSnum - parse signed integers from binary data
48 JpegMeta.Rational - A rational number class
51 JpegMeta.JpegFile - Primary class for Javascript parsing
55 this.JpegMeta = JpegMeta; // I have no clue why I need this magic... -- Bryan
58 parse an unsigned number of size bytes at offset in some binary string data.
60 is "<" parse the data as little endian, if endian
61 is ">" parse as big-endian.
63 JpegMeta.parseNum = function parseNum(endian, data, offset, size) {
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--) {
73 ret += data.charCodeAt(i);
79 parse an signed number of size bytes at offset in some binary string data.
81 is "<" parse the data as little endian, if endian
82 is ">" parse as big-endian.
84 JpegMeta.parseSnum = function parseSnum(endian, data, offset, size) {
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;
99 /* If it is negative we invert the bits */
100 ret += neg ? ~data.charCodeAt(i) & 0xff: data.charCodeAt(i);
103 /* If it is negative we do two's complement */
110 /* Rational number class */
111 JpegMeta.Rational = function Rational(num, den)
118 /* Rational number methods */
119 JpegMeta.Rational.prototype.toString = function toString() {
120 if (this.num === 0) {
121 return "" + this.num;
123 if (this.den === 1) {
124 return "" + this.num;
126 if (this.num === 1) {
127 return this.num + " / " + this.den;
129 return this.num / this.den; // + "/" + this.den;
132 JpegMeta.Rational.prototype.asFloat = function asFloat() {
133 return this.num / this.den;
136 /* MetaGroup class */
137 JpegMeta.MetaGroup = function MetaGroup(fieldName, description) {
138 this.fieldName = fieldName;
139 this.description = description;
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;
150 JpegMeta.MetaGroup.prototype.toString = function toString() {
151 return "[MetaGroup " + this.description + "]";
155 JpegMeta.MetaProp = function MetaProp(fieldName, description, value) {
156 this.fieldName = fieldName;
157 this.description = description;
162 JpegMeta.MetaProp.prototype.toString = function toString() {
163 return "" + this.value;
167 JpegMeta.JpegFile = function JpegFile(binary_data, filename) {
168 /* Change this to EOI if we want to parse. */
169 var break_segment = this._SOS;
171 this.metaGroups = {};
172 this._binary_data = binary_data;
173 this.filename = filename;
175 /* Go through and parse. */
177 var pos_start_of_segment = 0;
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) + ".");
195 while (pos < this._binary_data.length) {
196 delim = this._binary_data.charCodeAt(pos++);
197 mark = this._binary_data.charCodeAt(pos++);
199 pos_start_of_segment = pos;
201 if (delim != this._DELIM) {
205 if (mark === break_segment) {
209 headersize = JpegMeta.parseNum(">", this._binary_data, pos, 2);
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++);
224 segsize = pos - pos_start_of_segment;
226 if (this._markers[mark]) {
227 mark_code = this._markers[mark][0];
228 mark_fn = this._markers[mark][1];
235 this[mark_fn](mark, pos_start_of_segment + 2);
240 if (this.general === undefined) {
241 throw Error("Invalid JPEG file.");
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 + "]";
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;
261 this.JpegMeta.JpegFile.prototype._sofHandler = function _sofHandler (mark, pos) {
262 if (this.general !== undefined) {
263 throw Error("Unexpected multiple-frame image");
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]);
274 this.JpegMeta.JpegFile.prototype._JFIF_IDENT = "JFIF\x00";
275 this.JpegMeta.JpegFile.prototype._JFXX_IDENT = "JFXX\x00";
278 this.JpegMeta.JpegFile.prototype._EXIF_IDENT = "Exif\x00";
281 this.JpegMeta.JpegFile.prototype._types = {
282 /* The format is identifier : ["type name", type_size_in_bytes ] */
289 7 : ["UNDEFINED", 1],
292 10 : ["SRATIONAL", 8],
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"],
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"],
344 34665 : ["Exif tag", "ExifIfdPointer"],
345 34853 : ["GPS tag", "GPSInfoIfdPointer"]
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"],
354 /* B. Tag Relating to Image Data Characteristics */
355 40961 : ["Color Space", "ColorSpace"],
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"],
363 /* D. Tags Relating to User Information */
364 37500 : ["Manufacturer notes", "MakerNote"],
365 37510 : ["User comments", "UserComment"],
367 /* E. Tag Relating to Related File Information */
368 40964 : ["Related audio file", "RelatedSoundFile"],
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"],
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"],
419 42016 : ["Unique image ID", "ImageUniqueID"],
421 40965 : ["Interoperability tag", "InteroperabilityIFDPointer"]
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"]
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)"],
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)"],
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)"],
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)"],
482 /* Huffman table specification */
483 0xc4: ["DHT", null, "Define Huffman table(s)"],
484 0xcc: ["DAC", null, "Define arithmetic coding conditioning(s)"],
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”"],
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 */
537 /* Reserved markers */
538 0x01: ["JPG13", null] /* For temporary private use in arithmetic coding */
539 /* 02 -> bf are reserverd */
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;
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 */
556 var type, type_field, type_size;
566 group = this._addMetaGroup(name, description);
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) {
578 type = this._types[type_field][0];
579 type_size = this._types[type_field][1];
581 if (type_size * num_values <= 4) {
582 /* Data is in-line */
583 value_offset = tag_base + 8;
585 value_offset = base + value_offset;
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 */
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));
601 if (type == "SBYTE" || type == "SSHORT" || type == "SLONG") {
602 value.push(JpegMeta.parseSnum(endian, _binary_data, value_offset, type_size));
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));
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));
616 if (num_values === 1) {
620 if (tags[tag_field] !== undefined) {
621 group._addProperty(tags[tag_field][1], tags[tag_field][0], value);
626 this.JpegMeta.JpegFile.prototype._jfifHandler = function _jfifHandler(mark, pos) {
627 if (this.jfif !== undefined) {
628 throw Error("Multiple JFIF segments found");
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));
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 */
649 /* Don't know about other idents */
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);
659 /* Don't know about other idents */
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");
669 /* Parse this TIFF header */
673 var primary_ifd, exif_ifd, gps_ifd;
674 var endian_field = this._binary_data.slice(pos, pos + 2);
676 /* Trivia: This 'I' is for Intel, the 'M' is for Motorola */
677 if (endian_field === "II") {
679 } else if (endian_field === "MM") {
682 throw new Error("Malformed TIFF meta-data. Unknown endianess: " + endian_field);
685 magic_field = JpegMeta.parseNum(endian, this._binary_data, pos + 2, 2);
687 if (magic_field !== 42) {
688 throw new Error("Malformed TIFF meta-data. Bad magic: " + magic_field);
691 ifd_offset = JpegMeta.parseNum(endian, this._binary_data, pos + 4, 4);
694 this._parseIfd(endian, this._binary_data, pos, ifd_offset, this._tifftags, "tiff", "TIFF");
696 if (this.tiff.ExifIfdPointer) {
697 this._parseIfd(endian, this._binary_data, pos, this.tiff.ExifIfdPointer.value, this._exiftags, "exif", "Exif");
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) {
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;
710 this.gps._addProperty("latitude", "Dec. Latitude", latitude);
712 if (this.gps.GPSLongitude) {
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;
720 this.gps._addProperty("longitude", "Dec. Longitude", longitude);
725 /* JsJpegMeta ends here */
727 mw.libs.jpegmeta = function( fileReaderResult, fileName ) {
728 return new JpegMeta.JpegFile( fileReaderResult, fileName );