linted jpegmeta - commas cause IE errors. Not that this library is useful on IE,...
[lhc/web/wiklou.git] / resources / mediawiki.libs / mediawiki.libs.jpegmeta.js
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 */
3
4 (function( $ ) {
5
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 */
53
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 };
153
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 };
165
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 };
458
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 };
541
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 };
549
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 };
625
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 };
640
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 };
652
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 };
662
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 */
726
727 mw.libs.jpegmeta = function( fileReaderResult, fileName ) {
728 return new JpegMeta.JpegFile( fileReaderResult, fileName );
729 };
730
731 } )( jQuery );