Wrapping core modules (FIXME from r79929)
[lhc/web/wiklou.git] / resources / mediawiki.util / mediawiki.util.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( $, mw ) {
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
137 /* MetaGroup class */
138 JpegMeta.MetaGroup = function MetaGroup(fieldName, description) {
139 this.fieldName = fieldName;
140 this.description = description;
141 this.metaProps = {};
142 return this;
143 }
144
145 JpegMeta.MetaGroup.prototype._addProperty = function _addProperty(fieldName, description, value) {
146 var property = new JpegMeta.MetaProp(fieldName, description, value);
147 this[property.fieldName] = property;
148 this.metaProps[property.fieldName] = property;
149 }
150
151 JpegMeta.MetaGroup.prototype.toString = function toString() {
152 return "[MetaGroup " + this.description + "]";
153 }
154
155
156 /* MetaProp class */
157 JpegMeta.MetaProp = function MetaProp(fieldName, description, value) {
158 this.fieldName = fieldName;
159 this.description = description;
160 this.value = value;
161 return this;
162 }
163
164 JpegMeta.MetaProp.prototype.toString = function toString() {
165 return "" + this.value;
166 }
167
168
169
170 /* JpegFile class */
171 JpegMeta.JpegFile = function JpegFile(binary_data, filename) {
172 /* Change this to EOI if we want to parse. */
173 var break_segment = this._SOS;
174
175 this.metaGroups = {};
176 this._binary_data = binary_data;
177 this.filename = filename;
178
179 /* Go through and parse. */
180 var pos = 0;
181 var pos_start_of_segment = 0;
182 var delim;
183 var mark;
184 var _mark;
185 var segsize;
186 var headersize;
187 var mark_code;
188 var mark_fn;
189
190 /* Check to see if this looks like a JPEG file */
191 if (this._binary_data.slice(0, 2) !== this._SOI_MARKER) {
192 throw new Error("Doesn't look like a JPEG file. First two bytes are " +
193 this._binary_data.charCodeAt(0) + "," +
194 this._binary_data.charCodeAt(1) + ".");
195 }
196
197 pos += 2;
198
199 while (pos < this._binary_data.length) {
200 delim = this._binary_data.charCodeAt(pos++);
201 mark = this._binary_data.charCodeAt(pos++);
202
203 pos_start_of_segment = pos;
204
205 if (delim != this._DELIM) {
206 break;
207 }
208
209 if (mark === break_segment) {
210 break;
211 }
212
213 headersize = JpegMeta.parseNum(">", this._binary_data, pos, 2);
214
215 /* Find the end */
216 pos += headersize;
217 while (pos < this._binary_data.length) {
218 delim = this._binary_data.charCodeAt(pos++);
219 if (delim == this._DELIM) {
220 _mark = this._binary_data.charCodeAt(pos++);
221 if (_mark != 0x0) {
222 pos -= 2;
223 break;
224 }
225 }
226 }
227
228 segsize = pos - pos_start_of_segment;
229
230 if (this._markers[mark]) {
231 mark_code = this._markers[mark][0];
232 mark_fn = this._markers[mark][1];
233 } else {
234 mark_code = "UNKN";
235 mark_fn = undefined;
236 }
237
238 if (mark_fn) {
239 this[mark_fn](mark, pos_start_of_segment + 2);
240 }
241
242 }
243
244 if (this.general === undefined) {
245 throw Error("Invalid JPEG file.");
246 }
247
248 return this;
249 }
250
251 this.JpegMeta.JpegFile.prototype.toString = function () {
252 return "[JpegFile " + this.filename + " " +
253 this.general.type + " " +
254 this.general.pixelWidth + "x" +
255 this.general.pixelHeight +
256 " Depth: " + this.general.depth + "]";
257 }
258
259 /* Some useful constants */
260 this.JpegMeta.JpegFile.prototype._SOI_MARKER = '\xff\xd8';
261 this.JpegMeta.JpegFile.prototype._DELIM = 0xff;
262 this.JpegMeta.JpegFile.prototype._EOI = 0xd9;
263 this.JpegMeta.JpegFile.prototype._SOS = 0xda;
264
265 this.JpegMeta.JpegFile.prototype._sofHandler = function _sofHandler (mark, pos) {
266 if (this.general !== undefined) {
267 throw Error("Unexpected multiple-frame image");
268 }
269
270 this._addMetaGroup("general", "General");
271 this.general._addProperty("depth", "Depth", JpegMeta.parseNum(">", this._binary_data, pos, 1));
272 this.general._addProperty("pixelHeight", "Pixel Height", JpegMeta.parseNum(">", this._binary_data, pos + 1, 2));
273 this.general._addProperty("pixelWidth", "Pixel Width",JpegMeta.parseNum(">", this._binary_data, pos + 3, 2));
274 this.general._addProperty("type", "Type", this._markers[mark][2]);
275 }
276
277 /* JFIF idents */
278 this.JpegMeta.JpegFile.prototype._JFIF_IDENT = "JFIF\x00";
279 this.JpegMeta.JpegFile.prototype._JFXX_IDENT = "JFXX\x00";
280
281 /* EXIF idents */
282 this.JpegMeta.JpegFile.prototype._EXIF_IDENT = "Exif\x00";
283
284 /* TIFF types */
285 this.JpegMeta.JpegFile.prototype._types = {
286 /* The format is identifier : ["type name", type_size_in_bytes ] */
287 1 : ["BYTE", 1],
288 2 : ["ASCII", 1],
289 3 : ["SHORT", 2],
290 4 : ["LONG", 4],
291 5 : ["RATIONAL", 8],
292 6 : ["SBYTE", 1],
293 7 : ["UNDEFINED", 1],
294 8 : ["SSHORT", 2],
295 9 : ["SLONG", 4],
296 10 : ["SRATIONAL", 8],
297 11 : ["FLOAT", 4],
298 12 : ["DOUBLE", 8],
299 };
300
301 this.JpegMeta.JpegFile.prototype._tifftags = {
302 /* A. Tags relating to image data structure */
303 256 : ["Image width", "ImageWidth"],
304 257 : ["Image height", "ImageLength"],
305 258 : ["Number of bits per component", "BitsPerSample"],
306 259 : ["Compression scheme", "Compression",
307 {1 : "uncompressed", 6 : "JPEG compression" }],
308 262 : ["Pixel composition", "PhotmetricInerpretation",
309 {2 : "RGB", 6 : "YCbCr"}],
310 274 : ["Orientation of image", "Orientation",
311 /* FIXME: Check the mirror-image / reverse encoding and rotation */
312 {1 : "Normal", 2 : "Reverse?",
313 3 : "Upside-down", 4 : "Upside-down Reverse",
314 5 : "90 degree CW", 6 : "90 degree CW reverse",
315 7 : "90 degree CCW", 8 : "90 degree CCW reverse",}],
316 277 : ["Number of components", "SamplesPerPixel"],
317 284 : ["Image data arrangement", "PlanarConfiguration",
318 {1 : "chunky format", 2 : "planar format"}],
319 530 : ["Subsampling ratio of Y to C", "YCbCrSubSampling"],
320 531 : ["Y and C positioning", "YCbCrPositioning",
321 {1 : "centered", 2 : "co-sited"}],
322 282 : ["X Resolution", "XResolution"],
323 283 : ["Y Resolution", "YResolution"],
324 296 : ["Resolution Unit", "ResolutionUnit",
325 {2 : "inches", 3 : "centimeters"}],
326 /* B. Tags realting to recording offset */
327 273 : ["Image data location", "StripOffsets"],
328 278 : ["Number of rows per strip", "RowsPerStrip"],
329 279 : ["Bytes per compressed strip", "StripByteCounts"],
330 513 : ["Offset to JPEG SOI", "JPEGInterchangeFormat"],
331 514 : ["Bytes of JPEG Data", "JPEGInterchangeFormatLength"],
332 /* C. Tags relating to image data characteristics */
333 301 : ["Transfer function", "TransferFunction"],
334 318 : ["White point chromaticity", "WhitePoint"],
335 319 : ["Chromaticities of primaries", "PrimaryChromaticities"],
336 529 : ["Color space transformation matrix coefficients", "YCbCrCoefficients"],
337 532 : ["Pair of black and white reference values", "ReferenceBlackWhite"],
338 /* D. Other tags */
339 306 : ["Date and time", "DateTime"],
340 270 : ["Image title", "ImageDescription"],
341 271 : ["Make", "Make"],
342 272 : ["Model", "Model"],
343 305 : ["Software", "Software"],
344 315 : ["Person who created the image", "Artist"],
345 316 : ["Host Computer", "HostComputer"],
346 33432 : ["Copyright holder", "Copyright"],
347
348 34665 : ["Exif tag", "ExifIfdPointer"],
349 34853 : ["GPS tag", "GPSInfoIfdPointer"],
350 };
351
352 this.JpegMeta.JpegFile.prototype._exiftags = {
353 /* Tag Support Levels (2) - 0th IFX Exif Private Tags */
354 /* A. Tags Relating to Version */
355 36864 : ["Exif Version", "ExifVersion"],
356 40960 : ["FlashPix Version", "FlashpixVersion"],
357
358 /* B. Tag Relating to Image Data Characteristics */
359 40961 : ["Color Space", "ColorSpace"],
360
361 /* C. Tags Relating to Image Configuration */
362 37121 : ["Meaning of each component", "ComponentsConfiguration"],
363 37122 : ["Compressed Bits Per Pixel", "CompressedBitsPerPixel"],
364 40962 : ["Pixel X Dimension", "PixelXDimension"],
365 40963 : ["Pixel Y Dimension", "PixelYDimension"],
366
367 /* D. Tags Relating to User Information */
368 37500 : ["Manufacturer notes", "MakerNote"],
369 37510 : ["User comments", "UserComment"],
370
371 /* E. Tag Relating to Related File Information */
372 40964 : ["Related audio file", "RelatedSoundFile"],
373
374 /* F. Tags Relating to Date and Time */
375 36867 : ["Date Time Original", "DateTimeOriginal"],
376 36868 : ["Date Time Digitized", "DateTimeDigitized"],
377 37520 : ["DateTime subseconds", "SubSecTime"],
378 37521 : ["DateTimeOriginal subseconds", "SubSecTimeOriginal"],
379 37522 : ["DateTimeDigitized subseconds", "SubSecTimeDigitized"],
380
381 /* G. Tags Relating to Picture-Taking Conditions */
382 33434 : ["Exposure time", "ExposureTime"],
383 33437 : ["FNumber", "FNumber"],
384 34850 : ["Exposure program", "ExposureProgram"],
385 34852 : ["Spectral sensitivity", "SpectralSensitivity"],
386 34855 : ["ISO Speed Ratings", "ISOSpeedRatings"],
387 34856 : ["Optoelectric coefficient", "OECF"],
388 37377 : ["Shutter Speed", "ShutterSpeedValue"],
389 37378 : ["Aperture Value", "ApertureValue"],
390 37379 : ["Brightness", "BrightnessValue"],
391 37380 : ["Exposure Bias Value", "ExposureBiasValue"],
392 37381 : ["Max Aperture Value", "MaxApertureValue"],
393 37382 : ["Subject Distance", "SubjectDistance"],
394 37383 : ["Metering Mode", "MeteringMode"],
395 37384 : ["Light Source", "LightSource"],
396 37385 : ["Flash", "Flash"],
397 37386 : ["Focal Length", "FocalLength"],
398 37396 : ["Subject Area", "SubjectArea"],
399 41483 : ["Flash Energy", "FlashEnergy"],
400 41484 : ["Spatial Frequency Response", "SpatialFrequencyResponse"],
401 41486 : ["Focal Plane X Resolution", "FocalPlaneXResolution"],
402 41487 : ["Focal Plane Y Resolution", "FocalPlaneYResolution"],
403 41488 : ["Focal Plane Resolution Unit", "FocalPlaneResolutionUnit"],
404 41492 : ["Subject Location", "SubjectLocation"],
405 41493 : ["Exposure Index", "ExposureIndex"],
406 41495 : ["Sensing Method", "SensingMethod"],
407 41728 : ["File Source", "FileSource"],
408 41729 : ["Scene Type", "SceneType"],
409 41730 : ["CFA Pattern", "CFAPattern"],
410 41985 : ["Custom Rendered", "CustomRendered"],
411 41986 : ["Exposure Mode", "Exposure Mode"],
412 41987 : ["White Balance", "WhiteBalance"],
413 41988 : ["Digital Zoom Ratio", "DigitalZoomRatio"],
414 41990 : ["Scene Capture Type", "SceneCaptureType"],
415 41991 : ["Gain Control", "GainControl"],
416 41992 : ["Contrast", "Contrast"],
417 41993 : ["Saturation", "Saturation"],
418 41994 : ["Sharpness", "Sharpness"],
419 41995 : ["Device settings description", "DeviceSettingDescription"],
420 41996 : ["Subject distance range", "SubjectDistanceRange"],
421
422 /* H. Other Tags */
423 42016 : ["Unique image ID", "ImageUniqueID"],
424
425 40965 : ["Interoperability tag", "InteroperabilityIFDPointer"],
426 }
427
428 this.JpegMeta.JpegFile.prototype._gpstags = {
429 /* A. Tags Relating to GPS */
430 0 : ["GPS tag version", "GPSVersionID"],
431 1 : ["North or South Latitude", "GPSLatitudeRef"],
432 2 : ["Latitude", "GPSLatitude"],
433 3 : ["East or West Longitude", "GPSLongitudeRef"],
434 4 : ["Longitude", "GPSLongitude"],
435 5 : ["Altitude reference", "GPSAltitudeRef"],
436 6 : ["Altitude", "GPSAltitude"],
437 7 : ["GPS time (atomic clock)", "GPSTimeStamp"],
438 8 : ["GPS satellites usedd for measurement", "GPSSatellites"],
439 9 : ["GPS receiver status", "GPSStatus"],
440 10 : ["GPS mesaurement mode", "GPSMeasureMode"],
441 11 : ["Measurement precision", "GPSDOP"],
442 12 : ["Speed unit", "GPSSpeedRef"],
443 13 : ["Speed of GPS receiver", "GPSSpeed"],
444 14 : ["Reference for direction of movement", "GPSTrackRef"],
445 15 : ["Direction of movement", "GPSTrack"],
446 16 : ["Reference for direction of image", "GPSImgDirectionRef"],
447 17 : ["Direction of image", "GPSImgDirection"],
448 18 : ["Geodetic survey data used", "GPSMapDatum"],
449 19 : ["Reference for latitude of destination", "GPSDestLatitudeRef"],
450 20 : ["Latitude of destination", "GPSDestLatitude"],
451 21 : ["Reference for longitude of destination", "GPSDestLongitudeRef"],
452 22 : ["Longitude of destination", "GPSDestLongitude"],
453 23 : ["Reference for bearing of destination", "GPSDestBearingRef"],
454 24 : ["Bearing of destination", "GPSDestBearing"],
455 25 : ["Reference for distance to destination", "GPSDestDistanceRef"],
456 26 : ["Distance to destination", "GPSDestDistance"],
457 27 : ["Name of GPS processing method", "GPSProcessingMethod"],
458 28 : ["Name of GPS area", "GPSAreaInformation"],
459 29 : ["GPS Date", "GPSDateStamp"],
460 30 : ["GPS differential correction", "GPSDifferential"],
461 }
462
463
464 this.JpegMeta.JpegFile.prototype._markers = {
465 /* Start Of Frame markers, non-differential, Huffman coding */
466 0xc0: ["SOF0", "_sofHandler", "Baseline DCT"],
467 0xc1: ["SOF1", "_sofHandler", "Extended sequential DCT"],
468 0xc2: ["SOF2", "_sofHandler", "Progressive DCT"],
469 0xc3: ["SOF3", "_sofHandler", "Lossless (sequential)"],
470
471 /* Start Of Frame markers, differential, Huffman coding */
472 0xc5: ["SOF5", "_sofHandler", "Differential sequential DCT"],
473 0xc6: ["SOF6", "_sofHandler", "Differential progressive DCT"],
474 0xc7: ["SOF7", "_sofHandler", "Differential lossless (sequential)"],
475
476 /* Start Of Frame markers, non-differential, arithmetic coding */
477 0xc8: ["JPG", null, "Reserved for JPEG extensions"],
478 0xc9: ["SOF9", "_sofHandler", "Extended sequential DCT"],
479 0xca: ["SOF10", "_sofHandler", "Progressive DCT"],
480 0xcb: ["SOF11", "_sofHandler", "Lossless (sequential)"],
481
482 /* Start Of Frame markers, differential, arithmetic coding */
483 0xcd: ["SOF13", "_sofHandler", "Differential sequential DCT"],
484 0xce: ["SOF14", "_sofHandler", "Differential progressive DCT"],
485 0xcf: ["SOF15", "_sofHandler", "Differential lossless (sequential)"],
486
487 /* Huffman table specification */
488 0xc4: ["DHT", null, "Define Huffman table(s)"],
489 0xcc: ["DAC", null, "Define arithmetic coding conditioning(s)"],
490
491 /* Restart interval termination" */
492 0xd0: ["RST0", null, "Restart with modulo 8 count “0”"],
493 0xd1: ["RST1", null, "Restart with modulo 8 count “1”"],
494 0xd2: ["RST2", null, "Restart with modulo 8 count “2”"],
495 0xd3: ["RST3", null, "Restart with modulo 8 count “3”"],
496 0xd4: ["RST4", null, "Restart with modulo 8 count “4”"],
497 0xd5: ["RST5", null, "Restart with modulo 8 count “5”"],
498 0xd6: ["RST6", null, "Restart with modulo 8 count “6”"],
499 0xd7: ["RST7", null, "Restart with modulo 8 count “7”"],
500
501 /* Other markers */
502 0xd8: ["SOI", null, "Start of image"],
503 0xd9: ["EOI", null, "End of image"],
504 0xda: ["SOS", null, "Start of scan"],
505 0xdb: ["DQT", null, "Define quantization table(s)"],
506 0xdc: ["DNL", null, "Define number of lines"],
507 0xdd: ["DRI", null, "Define restart interval"],
508 0xde: ["DHP", null, "Define hierarchical progression"],
509 0xdf: ["EXP", null, "Expand reference component(s)"],
510 0xe0: ["APP0", "_app0Handler", "Reserved for application segments"],
511 0xe1: ["APP1", "_app1Handler"],
512 0xe2: ["APP2", null],
513 0xe3: ["APP3", null],
514 0xe4: ["APP4", null],
515 0xe5: ["APP5", null],
516 0xe6: ["APP6", null],
517 0xe7: ["APP7", null],
518 0xe8: ["APP8", null],
519 0xe9: ["APP9", null],
520 0xea: ["APP10", null],
521 0xeb: ["APP11", null],
522 0xec: ["APP12", null],
523 0xed: ["APP13", null],
524 0xee: ["APP14", null],
525 0xef: ["APP15", null],
526 0xf0: ["JPG0", null], /* Reserved for JPEG extensions */
527 0xf1: ["JPG1", null],
528 0xf2: ["JPG2", null],
529 0xf3: ["JPG3", null],
530 0xf4: ["JPG4", null],
531 0xf5: ["JPG5", null],
532 0xf6: ["JPG6", null],
533 0xf7: ["JPG7", null],
534 0xf8: ["JPG8", null],
535 0xf9: ["JPG9", null],
536 0xfa: ["JPG10", null],
537 0xfb: ["JPG11", null],
538 0xfc: ["JPG12", null],
539 0xfd: ["JPG13", null],
540 0xfe: ["COM", null], /* Comment */
541
542 /* Reserved markers */
543 0x01: ["JPG13", null], /* For temporary private use in arithmetic coding */
544 /* 02 -> bf are reserverd */
545 }
546
547 /* Private methods */
548 this.JpegMeta.JpegFile.prototype._addMetaGroup = function _addMetaGroup(name, description) {
549 var group = new JpegMeta.MetaGroup(name, description);
550 this[group.fieldName] = group;
551 this.metaGroups[group.fieldName] = group;
552 return group;
553 }
554
555 this.JpegMeta.JpegFile.prototype._parseIfd = function _parseIfd(endian, _binary_data, base, ifd_offset, tags, name, description) {
556 var num_fields = JpegMeta.parseNum(endian, _binary_data, base + ifd_offset, 2);
557 /* Per tag variables */
558 var i, j;
559 var tag_base;
560 var tag_field;
561 var type, type_field, type_size;
562 var num_values;
563 var value_offset;
564 var value;
565 var _val;
566 var num;
567 var den;
568
569 var group;
570
571 group = this._addMetaGroup(name, description);
572
573 for (var i = 0; i < num_fields; i++) {
574 /* parse the field */
575 tag_base = base + ifd_offset + 2 + (i * 12);
576 tag_field = JpegMeta.parseNum(endian, _binary_data, tag_base, 2);
577 type_field = JpegMeta.parseNum(endian, _binary_data, tag_base + 2, 2);
578 num_values = JpegMeta.parseNum(endian, _binary_data, tag_base + 4, 4);
579 value_offset = JpegMeta.parseNum(endian, _binary_data, tag_base + 8, 4);
580 if (this._types[type_field] === undefined) {
581 continue;
582 }
583 type = this._types[type_field][0];
584 type_size = this._types[type_field][1];
585
586 if (type_size * num_values <= 4) {
587 /* Data is in-line */
588 value_offset = tag_base + 8;
589 } else {
590 value_offset = base + value_offset;
591 }
592
593 /* Read the value */
594 if (type == "UNDEFINED") {
595 value = _binary_data.slice(value_offset, value_offset + num_values);
596 } else if (type == "ASCII") {
597 value = _binary_data.slice(value_offset, value_offset + num_values);
598 value = value.split('\x00')[0]
599 /* strip trail nul */
600 } else {
601 value = new Array();
602 for (j = 0; j < num_values; j++, value_offset += type_size) {
603 if (type == "BYTE" || type == "SHORT" || type == "LONG") {
604 value.push(JpegMeta.parseNum(endian, _binary_data, value_offset, type_size));
605 }
606 if (type == "SBYTE" || type == "SSHORT" || type == "SLONG") {
607 value.push(JpegMeta.parseSnum(endian, _binary_data, value_offset, type_size));
608 }
609 if (type == "RATIONAL") {
610 num = JpegMeta.parseNum(endian, _binary_data, value_offset, 4);
611 den = JpegMeta.parseNum(endian, _binary_data, value_offset + 4, 4);
612 value.push(new JpegMeta.Rational(num, den));
613 }
614 if (type == "SRATIONAL") {
615 num = JpegMeta.parseSnum(endian, _binary_data, value_offset, 4);
616 den = JpegMeta.parseSnum(endian, _binary_data, value_offset + 4, 4);
617 value.push(new JpegMeta.Rational(num, den));
618 }
619 value.push();
620 }
621 if (num_values === 1) {
622 value = value[0];
623 }
624 }
625 if (tags[tag_field] !== undefined) {
626 group._addProperty(tags[tag_field][1], tags[tag_field][0], value);
627 }
628 }
629 }
630
631 this.JpegMeta.JpegFile.prototype._jfifHandler = function _jfifHandler(mark, pos) {
632 if (this.jfif !== undefined) {
633 throw Error("Multiple JFIF segments found");
634 }
635 this._addMetaGroup("jfif", "JFIF");
636 this.jfif._addProperty("version_major", "Version Major", this._binary_data.charCodeAt(pos + 5));
637 this.jfif._addProperty("version_minor", "Version Minor", this._binary_data.charCodeAt(pos + 6));
638 this.jfif._addProperty("version", "JFIF Version", this.jfif.version_major.value + "." + this.jfif.version_minor.value);
639 this.jfif._addProperty("units", "Density Unit", this._binary_data.charCodeAt(pos + 7));
640 this.jfif._addProperty("Xdensity", "X density", JpegMeta.parseNum(">", this._binary_data, pos + 8, 2));
641 this.jfif._addProperty("Ydensity", "Y Density", JpegMeta.parseNum(">", this._binary_data, pos + 10, 2));
642 this.jfif._addProperty("Xthumbnail", "X Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 12, 1));
643 this.jfif._addProperty("Ythumbnail", "Y Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 13, 1));
644 }
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
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);
665 } else {
666 /* Don't know about other idents */
667 }
668 }
669
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");
674 }
675
676 /* Parse this TIFF header */
677 var endian;
678 var magic_field;
679 var ifd_offset;
680 var primary_ifd, exif_ifd, gps_ifd;
681 var endian_field = this._binary_data.slice(pos, pos + 2);
682
683 /* Trivia: This 'I' is for Intel, the 'M' is for Motorola */
684 if (endian_field === "II") {
685 endian = "<";
686 } else if (endian_field === "MM") {
687 endian = ">";
688 } else {
689 throw new Error("Malformed TIFF meta-data. Unknown endianess: " + endian_field);
690 }
691
692 magic_field = JpegMeta.parseNum(endian, this._binary_data, pos + 2, 2);
693
694 if (magic_field !== 42) {
695 throw new Error("Malformed TIFF meta-data. Bad magic: " + magic_field);
696 }
697
698 ifd_offset = JpegMeta.parseNum(endian, this._binary_data, pos + 4, 4);
699
700 /* Parse 0th IFD */
701 this._parseIfd(endian, this._binary_data, pos, ifd_offset, this._tifftags, "tiff", "TIFF");
702
703 if (this.tiff.ExifIfdPointer) {
704 this._parseIfd(endian, this._binary_data, pos, this.tiff.ExifIfdPointer.value, this._exiftags, "exif", "Exif");
705 }
706
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) {
710 var latitude;
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;
716 }
717 this.gps._addProperty("latitude", "Dec. Latitude", latitude);
718 }
719 if (this.gps.GPSLongitude) {
720 var longitude;
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;
726 }
727 this.gps._addProperty("longitude", "Dec. Longitude", longitude);
728 }
729 }
730 };
731
732 /* JsJpegMeta ends here */
733
734 mw.util.jpegmeta = function( fileReaderResult, fileName ) {
735 return new JpegMeta.JpegFile( fileReaderResult, fileName );
736 };
737
738 } )( jQuery, mediaWiki );