Index: trunk/phase3/includes/resourceloader/ResourceLoaderStartUpModule.php |
— | — | @@ -77,6 +77,9 @@ |
78 | 78 | 'wgSiteName' => $wgSitename, |
79 | 79 | 'wgFileExtensions' => array_values( $wgFileExtensions ), |
80 | 80 | 'wgDBname' => $wgDBname, |
| 81 | + // This sucks, it is only needed on Special:Upload, but I could |
| 82 | + // not find a way to add vars only for a certain module |
| 83 | + 'wgFileCanRotate' => BitmapHandler::canRotate(), |
81 | 84 | ); |
82 | 85 | if ( $wgContLang->hasVariants() ) { |
83 | 86 | $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); |
Index: trunk/phase3/includes/media/Bitmap.php |
— | — | @@ -22,10 +22,10 @@ |
23 | 23 | $srcWidth = $image->getWidth( $params['page'] ); |
24 | 24 | $srcHeight = $image->getHeight( $params['page'] ); |
25 | 25 | |
26 | | - if ( $this->canRotate() ) { |
| 26 | + if ( self::canRotate() ) { |
27 | 27 | $rotation = $this->getRotation( $image ); |
28 | 28 | if ( $rotation == 90 || $rotation == 270 ) { |
29 | | - wfDebug( __METHOD__ . ": Swapping width and height because the file will be rotation $rotation degrees\n" ); |
| 29 | + wfDebug( __METHOD__ . ": Swapping width and height because the file will be rotated $rotation degrees\n" ); |
30 | 30 | |
31 | 31 | $width = $params['width']; |
32 | 32 | $params['width'] = $params['height']; |
— | — | @@ -101,7 +101,7 @@ |
102 | 102 | } |
103 | 103 | |
104 | 104 | # Determine scaler type |
105 | | - $scaler = $this->getScalerType( $dstPath ); |
| 105 | + $scaler = self::getScalerType( $dstPath ); |
106 | 106 | wfDebug( __METHOD__ . ": scaler $scaler\n" ); |
107 | 107 | |
108 | 108 | if ( $scaler == 'client' ) { |
— | — | @@ -156,7 +156,7 @@ |
157 | 157 | * |
158 | 158 | * @return string client,im,custom,gd |
159 | 159 | */ |
160 | | - protected function getScalerType( $dstPath, $checkDstPath = true ) { |
| 160 | + protected static function getScalerType( $dstPath, $checkDstPath = true ) { |
161 | 161 | global $wgUseImageResize, $wgUseImageMagick, $wgCustomConvertCommand; |
162 | 162 | |
163 | 163 | if ( !$dstPath && $checkDstPath ) { |
— | — | @@ -678,8 +678,8 @@ |
679 | 679 | * |
680 | 680 | * @return bool |
681 | 681 | */ |
682 | | - public function canRotate() { |
683 | | - $scaler = $this->getScalerType( null, false ); |
| 682 | + public static function canRotate() { |
| 683 | + $scaler = self::getScalerType( null, false ); |
684 | 684 | return $scaler == 'im' || $scaler == 'gd'; |
685 | 685 | } |
686 | 686 | |
— | — | @@ -691,6 +691,6 @@ |
692 | 692 | * @return bool |
693 | 693 | */ |
694 | 694 | public function mustRender( $file ) { |
695 | | - return $this->canRotate() && $this->getRotation( $file ) != 0; |
| 695 | + return self::canRotate() && $this->getRotation( $file ) != 0; |
696 | 696 | } |
697 | 697 | } |
Index: trunk/phase3/includes/DefaultSettings.php |
— | — | @@ -1599,7 +1599,7 @@ |
1600 | 1600 | * to ensure that client-side caches do not keep obsolete copies of global |
1601 | 1601 | * styles. |
1602 | 1602 | */ |
1603 | | -$wgStyleVersion = '301'; |
| 1603 | +$wgStyleVersion = '302'; |
1604 | 1604 | |
1605 | 1605 | /** |
1606 | 1606 | * This will cache static pages for non-logged-in users to reduce |
Index: trunk/phase3/resources/mediawiki.util/mediawiki.util.jpegmeta.js |
— | — | @@ -0,0 +1,738 @@ |
| 2 | +/* This is JsJpegMeta 1.0, ported to MediaWiki ResourceLoader by Bryan Tong Minh */ |
| 3 | +/* The following lines where changed with respect to the original: 54, 625-627 */ |
| 4 | + |
| 5 | +( function( $, mw ) { |
| 6 | + |
| 7 | + /* JsJpegMeta starts here */ |
| 8 | + |
| 9 | + /* |
| 10 | + Copyright (c) 2009 Ben Leslie |
| 11 | + |
| 12 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 13 | + of this software and associated documentation files (the "Software"), to deal |
| 14 | + in the Software without restriction, including without limitation the rights |
| 15 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 16 | + copies of the Software, and to permit persons to whom the Software is |
| 17 | + furnished to do so, subject to the following conditions: |
| 18 | + |
| 19 | + The above copyright notice and this permission notice shall be included in |
| 20 | + all copies or substantial portions of the Software. |
| 21 | + |
| 22 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 23 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 24 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 25 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 26 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 27 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 28 | + THE SOFTWARE. |
| 29 | + */ |
| 30 | + |
| 31 | + /* |
| 32 | + This JavaScript library is used to parse meta-data from files |
| 33 | + with mime-type image/jpeg. |
| 34 | + |
| 35 | + Include it with something like: |
| 36 | + |
| 37 | + <script type="text/javascript" src="jpegmeta.js"></script> |
| 38 | + |
| 39 | + This adds a single 'module' object called 'JpegMeta' to the global |
| 40 | + namespace. |
| 41 | + |
| 42 | + Public Functions |
| 43 | + ---------------- |
| 44 | + JpegMeta.parseNum - parse unsigned integers from binary data |
| 45 | + JpegMeta.parseSnum - parse signed integers from binary data |
| 46 | + |
| 47 | + Public Classes |
| 48 | + -------------- |
| 49 | + JpegMeta.Rational - A rational number class |
| 50 | + JpegMeta.JfifSegment |
| 51 | + JpegMeta.ExifSegment |
| 52 | + JpegMeta.JpegFile - Primary class for Javascript parsing |
| 53 | + */ |
| 54 | + |
| 55 | + var JpegMeta = {}; |
| 56 | + this.JpegMeta = JpegMeta; // I have no clue why I need this magic... -- Bryan |
| 57 | + |
| 58 | + /* |
| 59 | + parse an unsigned number of size bytes at offset in some binary string data. |
| 60 | + If endian |
| 61 | + is "<" parse the data as little endian, if endian |
| 62 | + is ">" parse as big-endian. |
| 63 | + */ |
| 64 | + JpegMeta.parseNum = function parseNum(endian, data, offset, size) { |
| 65 | + var i; |
| 66 | + var ret; |
| 67 | + var big_endian = (endian === ">"); |
| 68 | + if (offset === undefined) offset = 0; |
| 69 | + if (size === undefined) size = data.length - offset; |
| 70 | + for (big_endian ? i = offset : i = offset + size - 1; |
| 71 | + big_endian ? i < offset + size : i >= offset; |
| 72 | + big_endian ? i++ : i--) { |
| 73 | + ret <<= 8; |
| 74 | + ret += data.charCodeAt(i); |
| 75 | + } |
| 76 | + return ret; |
| 77 | + } |
| 78 | + |
| 79 | + /* |
| 80 | + parse an signed number of size bytes at offset in some binary string data. |
| 81 | + If endian |
| 82 | + is "<" parse the data as little endian, if endian |
| 83 | + is ">" parse as big-endian. |
| 84 | + */ |
| 85 | + JpegMeta.parseSnum = function parseSnum(endian, data, offset, size) { |
| 86 | + var i; |
| 87 | + var ret; |
| 88 | + var neg; |
| 89 | + var big_endian = (endian === ">"); |
| 90 | + if (offset === undefined) offset = 0; |
| 91 | + if (size === undefined) size = data.length - offset; |
| 92 | + for (big_endian ? i = offset : i = offset + size - 1; |
| 93 | + big_endian ? i < offset + size : i >= offset; |
| 94 | + big_endian ? i++ : i--) { |
| 95 | + if (neg === undefined) { |
| 96 | + /* Negative if top bit is set */ |
| 97 | + neg = (data.charCodeAt(i) & 0x80) === 0x80; |
| 98 | + } |
| 99 | + ret <<= 8; |
| 100 | + /* If it is negative we invert the bits */ |
| 101 | + ret += neg ? ~data.charCodeAt(i) & 0xff: data.charCodeAt(i); |
| 102 | + } |
| 103 | + if (neg) { |
| 104 | + /* If it is negative we do two's complement */ |
| 105 | + ret += 1; |
| 106 | + ret *= -1; |
| 107 | + } |
| 108 | + return ret; |
| 109 | + } |
| 110 | + |
| 111 | + /* Rational number class */ |
| 112 | + JpegMeta.Rational = function Rational(num, den) |
| 113 | + { |
| 114 | + this.num = num; |
| 115 | + this.den = den || 1; |
| 116 | + return this; |
| 117 | + } |
| 118 | + |
| 119 | + /* Rational number methods */ |
| 120 | + JpegMeta.Rational.prototype.toString = function toString() { |
| 121 | + if (this.num === 0) { |
| 122 | + return "" + this.num |
| 123 | + } |
| 124 | + if (this.den === 1) { |
| 125 | + return "" + this.num; |
| 126 | + } |
| 127 | + if (this.num === 1) { |
| 128 | + return this.num + " / " + this.den; |
| 129 | + } |
| 130 | + return this.num / this.den; // + "/" + this.den; |
| 131 | + } |
| 132 | + |
| 133 | + JpegMeta.Rational.prototype.asFloat = function asFloat() { |
| 134 | + return this.num / this.den; |
| 135 | + } |
| 136 | + |
| 137 | + |
| 138 | + /* MetaGroup class */ |
| 139 | + JpegMeta.MetaGroup = function MetaGroup(fieldName, description) { |
| 140 | + this.fieldName = fieldName; |
| 141 | + this.description = description; |
| 142 | + this.metaProps = {}; |
| 143 | + return this; |
| 144 | + } |
| 145 | + |
| 146 | + JpegMeta.MetaGroup.prototype._addProperty = function _addProperty(fieldName, description, value) { |
| 147 | + var property = new JpegMeta.MetaProp(fieldName, description, value); |
| 148 | + this[property.fieldName] = property; |
| 149 | + this.metaProps[property.fieldName] = property; |
| 150 | + } |
| 151 | + |
| 152 | + JpegMeta.MetaGroup.prototype.toString = function toString() { |
| 153 | + return "[MetaGroup " + this.description + "]"; |
| 154 | + } |
| 155 | + |
| 156 | + |
| 157 | + /* MetaProp class */ |
| 158 | + JpegMeta.MetaProp = function MetaProp(fieldName, description, value) { |
| 159 | + this.fieldName = fieldName; |
| 160 | + this.description = description; |
| 161 | + this.value = value; |
| 162 | + return this; |
| 163 | + } |
| 164 | + |
| 165 | + JpegMeta.MetaProp.prototype.toString = function toString() { |
| 166 | + return "" + this.value; |
| 167 | + } |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + /* JpegFile class */ |
| 172 | + JpegMeta.JpegFile = function JpegFile(binary_data, filename) { |
| 173 | + /* Change this to EOI if we want to parse. */ |
| 174 | + var break_segment = this._SOS; |
| 175 | + |
| 176 | + this.metaGroups = {}; |
| 177 | + this._binary_data = binary_data; |
| 178 | + this.filename = filename; |
| 179 | + |
| 180 | + /* Go through and parse. */ |
| 181 | + var pos = 0; |
| 182 | + var pos_start_of_segment = 0; |
| 183 | + var delim; |
| 184 | + var mark; |
| 185 | + var _mark; |
| 186 | + var segsize; |
| 187 | + var headersize; |
| 188 | + var mark_code; |
| 189 | + var mark_fn; |
| 190 | + |
| 191 | + /* Check to see if this looks like a JPEG file */ |
| 192 | + if (this._binary_data.slice(0, 2) !== this._SOI_MARKER) { |
| 193 | + throw new Error("Doesn't look like a JPEG file. First two bytes are " + |
| 194 | + this._binary_data.charCodeAt(0) + "," + |
| 195 | + this._binary_data.charCodeAt(1) + "."); |
| 196 | + } |
| 197 | + |
| 198 | + pos += 2; |
| 199 | + |
| 200 | + while (pos < this._binary_data.length) { |
| 201 | + delim = this._binary_data.charCodeAt(pos++); |
| 202 | + mark = this._binary_data.charCodeAt(pos++); |
| 203 | + |
| 204 | + pos_start_of_segment = pos; |
| 205 | + |
| 206 | + if (delim != this._DELIM) { |
| 207 | + break; |
| 208 | + } |
| 209 | + |
| 210 | + if (mark === break_segment) { |
| 211 | + break; |
| 212 | + } |
| 213 | + |
| 214 | + headersize = JpegMeta.parseNum(">", this._binary_data, pos, 2); |
| 215 | + |
| 216 | + /* Find the end */ |
| 217 | + pos += headersize; |
| 218 | + while (pos < this._binary_data.length) { |
| 219 | + delim = this._binary_data.charCodeAt(pos++); |
| 220 | + if (delim == this._DELIM) { |
| 221 | + _mark = this._binary_data.charCodeAt(pos++); |
| 222 | + if (_mark != 0x0) { |
| 223 | + pos -= 2; |
| 224 | + break; |
| 225 | + } |
| 226 | + } |
| 227 | + } |
| 228 | + |
| 229 | + segsize = pos - pos_start_of_segment; |
| 230 | + |
| 231 | + if (this._markers[mark]) { |
| 232 | + mark_code = this._markers[mark][0]; |
| 233 | + mark_fn = this._markers[mark][1]; |
| 234 | + } else { |
| 235 | + mark_code = "UNKN"; |
| 236 | + mark_fn = undefined; |
| 237 | + } |
| 238 | + |
| 239 | + if (mark_fn) { |
| 240 | + this[mark_fn](mark, pos_start_of_segment + 2); |
| 241 | + } |
| 242 | + |
| 243 | + } |
| 244 | + |
| 245 | + if (this.general === undefined) { |
| 246 | + throw Error("Invalid JPEG file."); |
| 247 | + } |
| 248 | + |
| 249 | + return this; |
| 250 | + } |
| 251 | + |
| 252 | + this.JpegMeta.JpegFile.prototype.toString = function () { |
| 253 | + return "[JpegFile " + this.filename + " " + |
| 254 | + this.general.type + " " + |
| 255 | + this.general.pixelWidth + "x" + |
| 256 | + this.general.pixelHeight + |
| 257 | + " Depth: " + this.general.depth + "]"; |
| 258 | + } |
| 259 | + |
| 260 | + /* Some useful constants */ |
| 261 | + this.JpegMeta.JpegFile.prototype._SOI_MARKER = '\xff\xd8'; |
| 262 | + this.JpegMeta.JpegFile.prototype._DELIM = 0xff; |
| 263 | + this.JpegMeta.JpegFile.prototype._EOI = 0xd9; |
| 264 | + this.JpegMeta.JpegFile.prototype._SOS = 0xda; |
| 265 | + |
| 266 | + this.JpegMeta.JpegFile.prototype._sofHandler = function _sofHandler (mark, pos) { |
| 267 | + if (this.general !== undefined) { |
| 268 | + throw Error("Unexpected multiple-frame image"); |
| 269 | + } |
| 270 | + |
| 271 | + this._addMetaGroup("general", "General"); |
| 272 | + this.general._addProperty("depth", "Depth", JpegMeta.parseNum(">", this._binary_data, pos, 1)); |
| 273 | + this.general._addProperty("pixelHeight", "Pixel Height", JpegMeta.parseNum(">", this._binary_data, pos + 1, 2)); |
| 274 | + this.general._addProperty("pixelWidth", "Pixel Width",JpegMeta.parseNum(">", this._binary_data, pos + 3, 2)); |
| 275 | + this.general._addProperty("type", "Type", this._markers[mark][2]); |
| 276 | + } |
| 277 | + |
| 278 | + /* JFIF idents */ |
| 279 | + this.JpegMeta.JpegFile.prototype._JFIF_IDENT = "JFIF\x00"; |
| 280 | + this.JpegMeta.JpegFile.prototype._JFXX_IDENT = "JFXX\x00"; |
| 281 | + |
| 282 | + /* EXIF idents */ |
| 283 | + this.JpegMeta.JpegFile.prototype._EXIF_IDENT = "Exif\x00"; |
| 284 | + |
| 285 | + /* TIFF types */ |
| 286 | + this.JpegMeta.JpegFile.prototype._types = { |
| 287 | + /* The format is identifier : ["type name", type_size_in_bytes ] */ |
| 288 | + 1 : ["BYTE", 1], |
| 289 | + 2 : ["ASCII", 1], |
| 290 | + 3 : ["SHORT", 2], |
| 291 | + 4 : ["LONG", 4], |
| 292 | + 5 : ["RATIONAL", 8], |
| 293 | + 6 : ["SBYTE", 1], |
| 294 | + 7 : ["UNDEFINED", 1], |
| 295 | + 8 : ["SSHORT", 2], |
| 296 | + 9 : ["SLONG", 4], |
| 297 | + 10 : ["SRATIONAL", 8], |
| 298 | + 11 : ["FLOAT", 4], |
| 299 | + 12 : ["DOUBLE", 8], |
| 300 | + }; |
| 301 | + |
| 302 | + this.JpegMeta.JpegFile.prototype._tifftags = { |
| 303 | + /* A. Tags relating to image data structure */ |
| 304 | + 256 : ["Image width", "ImageWidth"], |
| 305 | + 257 : ["Image height", "ImageLength"], |
| 306 | + 258 : ["Number of bits per component", "BitsPerSample"], |
| 307 | + 259 : ["Compression scheme", "Compression", |
| 308 | + {1 : "uncompressed", 6 : "JPEG compression" }], |
| 309 | + 262 : ["Pixel composition", "PhotmetricInerpretation", |
| 310 | + {2 : "RGB", 6 : "YCbCr"}], |
| 311 | + 274 : ["Orientation of image", "Orientation", |
| 312 | + /* FIXME: Check the mirror-image / reverse encoding and rotation */ |
| 313 | + {1 : "Normal", 2 : "Reverse?", |
| 314 | + 3 : "Upside-down", 4 : "Upside-down Reverse", |
| 315 | + 5 : "90 degree CW", 6 : "90 degree CW reverse", |
| 316 | + 7 : "90 degree CCW", 8 : "90 degree CCW reverse",}], |
| 317 | + 277 : ["Number of components", "SamplesPerPixel"], |
| 318 | + 284 : ["Image data arrangement", "PlanarConfiguration", |
| 319 | + {1 : "chunky format", 2 : "planar format"}], |
| 320 | + 530 : ["Subsampling ratio of Y to C", "YCbCrSubSampling"], |
| 321 | + 531 : ["Y and C positioning", "YCbCrPositioning", |
| 322 | + {1 : "centered", 2 : "co-sited"}], |
| 323 | + 282 : ["X Resolution", "XResolution"], |
| 324 | + 283 : ["Y Resolution", "YResolution"], |
| 325 | + 296 : ["Resolution Unit", "ResolutionUnit", |
| 326 | + {2 : "inches", 3 : "centimeters"}], |
| 327 | + /* B. Tags realting to recording offset */ |
| 328 | + 273 : ["Image data location", "StripOffsets"], |
| 329 | + 278 : ["Number of rows per strip", "RowsPerStrip"], |
| 330 | + 279 : ["Bytes per compressed strip", "StripByteCounts"], |
| 331 | + 513 : ["Offset to JPEG SOI", "JPEGInterchangeFormat"], |
| 332 | + 514 : ["Bytes of JPEG Data", "JPEGInterchangeFormatLength"], |
| 333 | + /* C. Tags relating to image data characteristics */ |
| 334 | + 301 : ["Transfer function", "TransferFunction"], |
| 335 | + 318 : ["White point chromaticity", "WhitePoint"], |
| 336 | + 319 : ["Chromaticities of primaries", "PrimaryChromaticities"], |
| 337 | + 529 : ["Color space transformation matrix coefficients", "YCbCrCoefficients"], |
| 338 | + 532 : ["Pair of black and white reference values", "ReferenceBlackWhite"], |
| 339 | + /* D. Other tags */ |
| 340 | + 306 : ["Date and time", "DateTime"], |
| 341 | + 270 : ["Image title", "ImageDescription"], |
| 342 | + 271 : ["Make", "Make"], |
| 343 | + 272 : ["Model", "Model"], |
| 344 | + 305 : ["Software", "Software"], |
| 345 | + 315 : ["Person who created the image", "Artist"], |
| 346 | + 316 : ["Host Computer", "HostComputer"], |
| 347 | + 33432 : ["Copyright holder", "Copyright"], |
| 348 | + |
| 349 | + 34665 : ["Exif tag", "ExifIfdPointer"], |
| 350 | + 34853 : ["GPS tag", "GPSInfoIfdPointer"], |
| 351 | + }; |
| 352 | + |
| 353 | + this.JpegMeta.JpegFile.prototype._exiftags = { |
| 354 | + /* Tag Support Levels (2) - 0th IFX Exif Private Tags */ |
| 355 | + /* A. Tags Relating to Version */ |
| 356 | + 36864 : ["Exif Version", "ExifVersion"], |
| 357 | + 40960 : ["FlashPix Version", "FlashpixVersion"], |
| 358 | + |
| 359 | + /* B. Tag Relating to Image Data Characteristics */ |
| 360 | + 40961 : ["Color Space", "ColorSpace"], |
| 361 | + |
| 362 | + /* C. Tags Relating to Image Configuration */ |
| 363 | + 37121 : ["Meaning of each component", "ComponentsConfiguration"], |
| 364 | + 37122 : ["Compressed Bits Per Pixel", "CompressedBitsPerPixel"], |
| 365 | + 40962 : ["Pixel X Dimension", "PixelXDimension"], |
| 366 | + 40963 : ["Pixel Y Dimension", "PixelYDimension"], |
| 367 | + |
| 368 | + /* D. Tags Relating to User Information */ |
| 369 | + 37500 : ["Manufacturer notes", "MakerNote"], |
| 370 | + 37510 : ["User comments", "UserComment"], |
| 371 | + |
| 372 | + /* E. Tag Relating to Related File Information */ |
| 373 | + 40964 : ["Related audio file", "RelatedSoundFile"], |
| 374 | + |
| 375 | + /* F. Tags Relating to Date and Time */ |
| 376 | + 36867 : ["Date Time Original", "DateTimeOriginal"], |
| 377 | + 36868 : ["Date Time Digitized", "DateTimeDigitized"], |
| 378 | + 37520 : ["DateTime subseconds", "SubSecTime"], |
| 379 | + 37521 : ["DateTimeOriginal subseconds", "SubSecTimeOriginal"], |
| 380 | + 37522 : ["DateTimeDigitized subseconds", "SubSecTimeDigitized"], |
| 381 | + |
| 382 | + /* G. Tags Relating to Picture-Taking Conditions */ |
| 383 | + 33434 : ["Exposure time", "ExposureTime"], |
| 384 | + 33437 : ["FNumber", "FNumber"], |
| 385 | + 34850 : ["Exposure program", "ExposureProgram"], |
| 386 | + 34852 : ["Spectral sensitivity", "SpectralSensitivity"], |
| 387 | + 34855 : ["ISO Speed Ratings", "ISOSpeedRatings"], |
| 388 | + 34856 : ["Optoelectric coefficient", "OECF"], |
| 389 | + 37377 : ["Shutter Speed", "ShutterSpeedValue"], |
| 390 | + 37378 : ["Aperture Value", "ApertureValue"], |
| 391 | + 37379 : ["Brightness", "BrightnessValue"], |
| 392 | + 37380 : ["Exposure Bias Value", "ExposureBiasValue"], |
| 393 | + 37381 : ["Max Aperture Value", "MaxApertureValue"], |
| 394 | + 37382 : ["Subject Distance", "SubjectDistance"], |
| 395 | + 37383 : ["Metering Mode", "MeteringMode"], |
| 396 | + 37384 : ["Light Source", "LightSource"], |
| 397 | + 37385 : ["Flash", "Flash"], |
| 398 | + 37386 : ["Focal Length", "FocalLength"], |
| 399 | + 37396 : ["Subject Area", "SubjectArea"], |
| 400 | + 41483 : ["Flash Energy", "FlashEnergy"], |
| 401 | + 41484 : ["Spatial Frequency Response", "SpatialFrequencyResponse"], |
| 402 | + 41486 : ["Focal Plane X Resolution", "FocalPlaneXResolution"], |
| 403 | + 41487 : ["Focal Plane Y Resolution", "FocalPlaneYResolution"], |
| 404 | + 41488 : ["Focal Plane Resolution Unit", "FocalPlaneResolutionUnit"], |
| 405 | + 41492 : ["Subject Location", "SubjectLocation"], |
| 406 | + 41493 : ["Exposure Index", "ExposureIndex"], |
| 407 | + 41495 : ["Sensing Method", "SensingMethod"], |
| 408 | + 41728 : ["File Source", "FileSource"], |
| 409 | + 41729 : ["Scene Type", "SceneType"], |
| 410 | + 41730 : ["CFA Pattern", "CFAPattern"], |
| 411 | + 41985 : ["Custom Rendered", "CustomRendered"], |
| 412 | + 41986 : ["Exposure Mode", "Exposure Mode"], |
| 413 | + 41987 : ["White Balance", "WhiteBalance"], |
| 414 | + 41988 : ["Digital Zoom Ratio", "DigitalZoomRatio"], |
| 415 | + 41990 : ["Scene Capture Type", "SceneCaptureType"], |
| 416 | + 41991 : ["Gain Control", "GainControl"], |
| 417 | + 41992 : ["Contrast", "Contrast"], |
| 418 | + 41993 : ["Saturation", "Saturation"], |
| 419 | + 41994 : ["Sharpness", "Sharpness"], |
| 420 | + 41995 : ["Device settings description", "DeviceSettingDescription"], |
| 421 | + 41996 : ["Subject distance range", "SubjectDistanceRange"], |
| 422 | + |
| 423 | + /* H. Other Tags */ |
| 424 | + 42016 : ["Unique image ID", "ImageUniqueID"], |
| 425 | + |
| 426 | + 40965 : ["Interoperability tag", "InteroperabilityIFDPointer"], |
| 427 | + } |
| 428 | + |
| 429 | + this.JpegMeta.JpegFile.prototype._gpstags = { |
| 430 | + /* A. Tags Relating to GPS */ |
| 431 | + 0 : ["GPS tag version", "GPSVersionID"], |
| 432 | + 1 : ["North or South Latitude", "GPSLatitudeRef"], |
| 433 | + 2 : ["Latitude", "GPSLatitude"], |
| 434 | + 3 : ["East or West Longitude", "GPSLongitudeRef"], |
| 435 | + 4 : ["Longitude", "GPSLongitude"], |
| 436 | + 5 : ["Altitude reference", "GPSAltitudeRef"], |
| 437 | + 6 : ["Altitude", "GPSAltitude"], |
| 438 | + 7 : ["GPS time (atomic clock)", "GPSTimeStamp"], |
| 439 | + 8 : ["GPS satellites usedd for measurement", "GPSSatellites"], |
| 440 | + 9 : ["GPS receiver status", "GPSStatus"], |
| 441 | + 10 : ["GPS mesaurement mode", "GPSMeasureMode"], |
| 442 | + 11 : ["Measurement precision", "GPSDOP"], |
| 443 | + 12 : ["Speed unit", "GPSSpeedRef"], |
| 444 | + 13 : ["Speed of GPS receiver", "GPSSpeed"], |
| 445 | + 14 : ["Reference for direction of movement", "GPSTrackRef"], |
| 446 | + 15 : ["Direction of movement", "GPSTrack"], |
| 447 | + 16 : ["Reference for direction of image", "GPSImgDirectionRef"], |
| 448 | + 17 : ["Direction of image", "GPSImgDirection"], |
| 449 | + 18 : ["Geodetic survey data used", "GPSMapDatum"], |
| 450 | + 19 : ["Reference for latitude of destination", "GPSDestLatitudeRef"], |
| 451 | + 20 : ["Latitude of destination", "GPSDestLatitude"], |
| 452 | + 21 : ["Reference for longitude of destination", "GPSDestLongitudeRef"], |
| 453 | + 22 : ["Longitude of destination", "GPSDestLongitude"], |
| 454 | + 23 : ["Reference for bearing of destination", "GPSDestBearingRef"], |
| 455 | + 24 : ["Bearing of destination", "GPSDestBearing"], |
| 456 | + 25 : ["Reference for distance to destination", "GPSDestDistanceRef"], |
| 457 | + 26 : ["Distance to destination", "GPSDestDistance"], |
| 458 | + 27 : ["Name of GPS processing method", "GPSProcessingMethod"], |
| 459 | + 28 : ["Name of GPS area", "GPSAreaInformation"], |
| 460 | + 29 : ["GPS Date", "GPSDateStamp"], |
| 461 | + 30 : ["GPS differential correction", "GPSDifferential"], |
| 462 | + } |
| 463 | + |
| 464 | + |
| 465 | + this.JpegMeta.JpegFile.prototype._markers = { |
| 466 | + /* Start Of Frame markers, non-differential, Huffman coding */ |
| 467 | + 0xc0: ["SOF0", "_sofHandler", "Baseline DCT"], |
| 468 | + 0xc1: ["SOF1", "_sofHandler", "Extended sequential DCT"], |
| 469 | + 0xc2: ["SOF2", "_sofHandler", "Progressive DCT"], |
| 470 | + 0xc3: ["SOF3", "_sofHandler", "Lossless (sequential)"], |
| 471 | + |
| 472 | + /* Start Of Frame markers, differential, Huffman coding */ |
| 473 | + 0xc5: ["SOF5", "_sofHandler", "Differential sequential DCT"], |
| 474 | + 0xc6: ["SOF6", "_sofHandler", "Differential progressive DCT"], |
| 475 | + 0xc7: ["SOF7", "_sofHandler", "Differential lossless (sequential)"], |
| 476 | + |
| 477 | + /* Start Of Frame markers, non-differential, arithmetic coding */ |
| 478 | + 0xc8: ["JPG", null, "Reserved for JPEG extensions"], |
| 479 | + 0xc9: ["SOF9", "_sofHandler", "Extended sequential DCT"], |
| 480 | + 0xca: ["SOF10", "_sofHandler", "Progressive DCT"], |
| 481 | + 0xcb: ["SOF11", "_sofHandler", "Lossless (sequential)"], |
| 482 | + |
| 483 | + /* Start Of Frame markers, differential, arithmetic coding */ |
| 484 | + 0xcd: ["SOF13", "_sofHandler", "Differential sequential DCT"], |
| 485 | + 0xce: ["SOF14", "_sofHandler", "Differential progressive DCT"], |
| 486 | + 0xcf: ["SOF15", "_sofHandler", "Differential lossless (sequential)"], |
| 487 | + |
| 488 | + /* Huffman table specification */ |
| 489 | + 0xc4: ["DHT", null, "Define Huffman table(s)"], |
| 490 | + 0xcc: ["DAC", null, "Define arithmetic coding conditioning(s)"], |
| 491 | + |
| 492 | + /* Restart interval termination" */ |
| 493 | + 0xd0: ["RST0", null, "Restart with modulo 8 count “0”"], |
| 494 | + 0xd1: ["RST1", null, "Restart with modulo 8 count “1”"], |
| 495 | + 0xd2: ["RST2", null, "Restart with modulo 8 count “2”"], |
| 496 | + 0xd3: ["RST3", null, "Restart with modulo 8 count “3”"], |
| 497 | + 0xd4: ["RST4", null, "Restart with modulo 8 count “4”"], |
| 498 | + 0xd5: ["RST5", null, "Restart with modulo 8 count “5”"], |
| 499 | + 0xd6: ["RST6", null, "Restart with modulo 8 count “6”"], |
| 500 | + 0xd7: ["RST7", null, "Restart with modulo 8 count “7”"], |
| 501 | + |
| 502 | + /* Other markers */ |
| 503 | + 0xd8: ["SOI", null, "Start of image"], |
| 504 | + 0xd9: ["EOI", null, "End of image"], |
| 505 | + 0xda: ["SOS", null, "Start of scan"], |
| 506 | + 0xdb: ["DQT", null, "Define quantization table(s)"], |
| 507 | + 0xdc: ["DNL", null, "Define number of lines"], |
| 508 | + 0xdd: ["DRI", null, "Define restart interval"], |
| 509 | + 0xde: ["DHP", null, "Define hierarchical progression"], |
| 510 | + 0xdf: ["EXP", null, "Expand reference component(s)"], |
| 511 | + 0xe0: ["APP0", "_app0Handler", "Reserved for application segments"], |
| 512 | + 0xe1: ["APP1", "_app1Handler"], |
| 513 | + 0xe2: ["APP2", null], |
| 514 | + 0xe3: ["APP3", null], |
| 515 | + 0xe4: ["APP4", null], |
| 516 | + 0xe5: ["APP5", null], |
| 517 | + 0xe6: ["APP6", null], |
| 518 | + 0xe7: ["APP7", null], |
| 519 | + 0xe8: ["APP8", null], |
| 520 | + 0xe9: ["APP9", null], |
| 521 | + 0xea: ["APP10", null], |
| 522 | + 0xeb: ["APP11", null], |
| 523 | + 0xec: ["APP12", null], |
| 524 | + 0xed: ["APP13", null], |
| 525 | + 0xee: ["APP14", null], |
| 526 | + 0xef: ["APP15", null], |
| 527 | + 0xf0: ["JPG0", null], /* Reserved for JPEG extensions */ |
| 528 | + 0xf1: ["JPG1", null], |
| 529 | + 0xf2: ["JPG2", null], |
| 530 | + 0xf3: ["JPG3", null], |
| 531 | + 0xf4: ["JPG4", null], |
| 532 | + 0xf5: ["JPG5", null], |
| 533 | + 0xf6: ["JPG6", null], |
| 534 | + 0xf7: ["JPG7", null], |
| 535 | + 0xf8: ["JPG8", null], |
| 536 | + 0xf9: ["JPG9", null], |
| 537 | + 0xfa: ["JPG10", null], |
| 538 | + 0xfb: ["JPG11", null], |
| 539 | + 0xfc: ["JPG12", null], |
| 540 | + 0xfd: ["JPG13", null], |
| 541 | + 0xfe: ["COM", null], /* Comment */ |
| 542 | + |
| 543 | + /* Reserved markers */ |
| 544 | + 0x01: ["JPG13", null], /* For temporary private use in arithmetic coding */ |
| 545 | + /* 02 -> bf are reserverd */ |
| 546 | + } |
| 547 | + |
| 548 | + /* Private methods */ |
| 549 | + this.JpegMeta.JpegFile.prototype._addMetaGroup = function _addMetaGroup(name, description) { |
| 550 | + var group = new JpegMeta.MetaGroup(name, description); |
| 551 | + this[group.fieldName] = group; |
| 552 | + this.metaGroups[group.fieldName] = group; |
| 553 | + return group; |
| 554 | + } |
| 555 | + |
| 556 | + this.JpegMeta.JpegFile.prototype._parseIfd = function _parseIfd(endian, _binary_data, base, ifd_offset, tags, name, description) { |
| 557 | + var num_fields = JpegMeta.parseNum(endian, _binary_data, base + ifd_offset, 2); |
| 558 | + /* Per tag variables */ |
| 559 | + var i, j; |
| 560 | + var tag_base; |
| 561 | + var tag_field; |
| 562 | + var type, type_field, type_size; |
| 563 | + var num_values; |
| 564 | + var value_offset; |
| 565 | + var value; |
| 566 | + var _val; |
| 567 | + var num; |
| 568 | + var den; |
| 569 | + |
| 570 | + var group; |
| 571 | + |
| 572 | + group = this._addMetaGroup(name, description); |
| 573 | + |
| 574 | + for (var i = 0; i < num_fields; i++) { |
| 575 | + /* parse the field */ |
| 576 | + tag_base = base + ifd_offset + 2 + (i * 12); |
| 577 | + tag_field = JpegMeta.parseNum(endian, _binary_data, tag_base, 2); |
| 578 | + type_field = JpegMeta.parseNum(endian, _binary_data, tag_base + 2, 2); |
| 579 | + num_values = JpegMeta.parseNum(endian, _binary_data, tag_base + 4, 4); |
| 580 | + value_offset = JpegMeta.parseNum(endian, _binary_data, tag_base + 8, 4); |
| 581 | + if (this._types[type_field] === undefined) { |
| 582 | + continue; |
| 583 | + } |
| 584 | + type = this._types[type_field][0]; |
| 585 | + type_size = this._types[type_field][1]; |
| 586 | + |
| 587 | + if (type_size * num_values <= 4) { |
| 588 | + /* Data is in-line */ |
| 589 | + value_offset = tag_base + 8; |
| 590 | + } else { |
| 591 | + value_offset = base + value_offset; |
| 592 | + } |
| 593 | + |
| 594 | + /* Read the value */ |
| 595 | + if (type == "UNDEFINED") { |
| 596 | + value = _binary_data.slice(value_offset, value_offset + num_values); |
| 597 | + } else if (type == "ASCII") { |
| 598 | + value = _binary_data.slice(value_offset, value_offset + num_values); |
| 599 | + value = value.split('\x00')[0] |
| 600 | + /* strip trail nul */ |
| 601 | + } else { |
| 602 | + value = new Array(); |
| 603 | + for (j = 0; j < num_values; j++, value_offset += type_size) { |
| 604 | + if (type == "BYTE" || type == "SHORT" || type == "LONG") { |
| 605 | + value.push(JpegMeta.parseNum(endian, _binary_data, value_offset, type_size)); |
| 606 | + } |
| 607 | + if (type == "SBYTE" || type == "SSHORT" || type == "SLONG") { |
| 608 | + value.push(JpegMeta.parseSnum(endian, _binary_data, value_offset, type_size)); |
| 609 | + } |
| 610 | + if (type == "RATIONAL") { |
| 611 | + num = JpegMeta.parseNum(endian, _binary_data, value_offset, 4); |
| 612 | + den = JpegMeta.parseNum(endian, _binary_data, value_offset + 4, 4); |
| 613 | + value.push(new JpegMeta.Rational(num, den)); |
| 614 | + } |
| 615 | + if (type == "SRATIONAL") { |
| 616 | + num = JpegMeta.parseSnum(endian, _binary_data, value_offset, 4); |
| 617 | + den = JpegMeta.parseSnum(endian, _binary_data, value_offset + 4, 4); |
| 618 | + value.push(new JpegMeta.Rational(num, den)); |
| 619 | + } |
| 620 | + value.push(); |
| 621 | + } |
| 622 | + if (num_values === 1) { |
| 623 | + value = value[0]; |
| 624 | + } |
| 625 | + } |
| 626 | + if (tags[tag_field] !== undefined) { |
| 627 | + group._addProperty(tags[tag_field][1], tags[tag_field][0], value); |
| 628 | + } |
| 629 | + } |
| 630 | + } |
| 631 | + |
| 632 | + this.JpegMeta.JpegFile.prototype._jfifHandler = function _jfifHandler(mark, pos) { |
| 633 | + if (this.jfif !== undefined) { |
| 634 | + throw Error("Multiple JFIF segments found"); |
| 635 | + } |
| 636 | + this._addMetaGroup("jfif", "JFIF"); |
| 637 | + this.jfif._addProperty("version_major", "Version Major", this._binary_data.charCodeAt(pos + 5)); |
| 638 | + this.jfif._addProperty("version_minor", "Version Minor", this._binary_data.charCodeAt(pos + 6)); |
| 639 | + this.jfif._addProperty("version", "JFIF Version", this.jfif.version_major.value + "." + this.jfif.version_minor.value); |
| 640 | + this.jfif._addProperty("units", "Density Unit", this._binary_data.charCodeAt(pos + 7)); |
| 641 | + this.jfif._addProperty("Xdensity", "X density", JpegMeta.parseNum(">", this._binary_data, pos + 8, 2)); |
| 642 | + this.jfif._addProperty("Ydensity", "Y Density", JpegMeta.parseNum(">", this._binary_data, pos + 10, 2)); |
| 643 | + this.jfif._addProperty("Xthumbnail", "X Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 12, 1)); |
| 644 | + this.jfif._addProperty("Ythumbnail", "Y Thumbnail", JpegMeta.parseNum(">", this._binary_data, pos + 13, 1)); |
| 645 | + } |
| 646 | + |
| 647 | + |
| 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 */ |
| 655 | + } else { |
| 656 | + /* Don't know about other idents */ |
| 657 | + } |
| 658 | + } |
| 659 | + |
| 660 | + |
| 661 | + /* Handle app1 segments */ |
| 662 | + this.JpegMeta.JpegFile.prototype._app1Handler = function _app1Handler(mark, pos) { |
| 663 | + var ident = this._binary_data.slice(pos, pos + 5); |
| 664 | + if (ident == this._EXIF_IDENT) { |
| 665 | + this._exifHandler(mark, pos + 6); |
| 666 | + } else { |
| 667 | + /* Don't know about other idents */ |
| 668 | + } |
| 669 | + } |
| 670 | + |
| 671 | + /* Handle exif segments */ |
| 672 | + JpegMeta.JpegFile.prototype._exifHandler = function _exifHandler(mark, pos) { |
| 673 | + if (this.exif !== undefined) { |
| 674 | + throw new Error("Multiple JFIF segments found"); |
| 675 | + } |
| 676 | + |
| 677 | + /* Parse this TIFF header */ |
| 678 | + var endian; |
| 679 | + var magic_field; |
| 680 | + var ifd_offset; |
| 681 | + var primary_ifd, exif_ifd, gps_ifd; |
| 682 | + var endian_field = this._binary_data.slice(pos, pos + 2); |
| 683 | + |
| 684 | + /* Trivia: This 'I' is for Intel, the 'M' is for Motorola */ |
| 685 | + if (endian_field === "II") { |
| 686 | + endian = "<"; |
| 687 | + } else if (endian_field === "MM") { |
| 688 | + endian = ">"; |
| 689 | + } else { |
| 690 | + throw new Error("Malformed TIFF meta-data. Unknown endianess: " + endian_field); |
| 691 | + } |
| 692 | + |
| 693 | + magic_field = JpegMeta.parseNum(endian, this._binary_data, pos + 2, 2); |
| 694 | + |
| 695 | + if (magic_field !== 42) { |
| 696 | + throw new Error("Malformed TIFF meta-data. Bad magic: " + magic_field); |
| 697 | + } |
| 698 | + |
| 699 | + ifd_offset = JpegMeta.parseNum(endian, this._binary_data, pos + 4, 4); |
| 700 | + |
| 701 | + /* Parse 0th IFD */ |
| 702 | + this._parseIfd(endian, this._binary_data, pos, ifd_offset, this._tifftags, "tiff", "TIFF"); |
| 703 | + |
| 704 | + if (this.tiff.ExifIfdPointer) { |
| 705 | + this._parseIfd(endian, this._binary_data, pos, this.tiff.ExifIfdPointer.value, this._exiftags, "exif", "Exif"); |
| 706 | + } |
| 707 | + |
| 708 | + if (this.tiff.GPSInfoIfdPointer) { |
| 709 | + this._parseIfd(endian, this._binary_data, pos, this.tiff.GPSInfoIfdPointer.value, this._gpstags, "gps", "GPS"); |
| 710 | + if (this.gps.GPSLatitude) { |
| 711 | + var latitude; |
| 712 | + latitude = this.gps.GPSLatitude.value[0].asFloat() + |
| 713 | + (1 / 60) * this.gps.GPSLatitude.value[1].asFloat() + |
| 714 | + (1 / 3600) * this.gps.GPSLatitude.value[2].asFloat(); |
| 715 | + if (this.gps.GPSLatitudeRef.value === "S") { |
| 716 | + latitude = -latitude; |
| 717 | + } |
| 718 | + this.gps._addProperty("latitude", "Dec. Latitude", latitude); |
| 719 | + } |
| 720 | + if (this.gps.GPSLongitude) { |
| 721 | + var longitude; |
| 722 | + longitude = this.gps.GPSLongitude.value[0].asFloat() + |
| 723 | + (1 / 60) * this.gps.GPSLongitude.value[1].asFloat() + |
| 724 | + (1 / 3600) * this.gps.GPSLongitude.value[2].asFloat(); |
| 725 | + if (this.gps.GPSLongitudeRef.value === "W") { |
| 726 | + longitude = -longitude; |
| 727 | + } |
| 728 | + this.gps._addProperty("longitude", "Dec. Longitude", longitude); |
| 729 | + } |
| 730 | + } |
| 731 | + }; |
| 732 | + |
| 733 | + /* JsJpegMeta ends here */ |
| 734 | + |
| 735 | + mw.util.jpegmeta = function( fileReaderResult, fileName ) { |
| 736 | + return new JpegMeta.JpegFile( fileReaderResult, fileName ); |
| 737 | + }; |
| 738 | + |
| 739 | +} )( jQuery, mediaWiki ); |
Property changes on: trunk/phase3/resources/mediawiki.util/mediawiki.util.jpegmeta.js |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 740 | + native |
Index: trunk/phase3/resources/Resources.php |
— | — | @@ -355,6 +355,9 @@ |
356 | 356 | 'dependencies' => array( 'jquery.checkboxShiftClick', 'jquery.client', 'jquery.cookie', 'jquery.placeholder', 'jquery.makeCollapsible' ), |
357 | 357 | 'debugScripts' => 'resources/mediawiki.util/mediawiki.util.test.js', |
358 | 358 | ), |
| 359 | + 'mediawiki.util.jpegmeta' => array( |
| 360 | + 'scripts' => 'resources/mediawiki.util/mediawiki.util.jpegmeta.js', |
| 361 | + ), |
359 | 362 | 'mediawiki.action.history' => array( |
360 | 363 | 'scripts' => 'resources/mediawiki.action/mediawiki.action.history.js', |
361 | 364 | 'dependencies' => 'mediawiki.legacy.history', |
— | — | @@ -385,6 +388,7 @@ |
386 | 389 | // @todo: merge in remainder of mediawiki.legacy.upload |
387 | 390 | 'scripts' => 'resources/mediawiki.special/mediawiki.special.upload.js', |
388 | 391 | 'messages' => array( 'widthheight', 'size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes' ), |
| 392 | + 'dependencies' => array( 'mediawiki.util.jpegmeta' ), |
389 | 393 | ), |
390 | 394 | 'mediawiki.language' => array( |
391 | 395 | 'scripts' => 'resources/mediawiki.language/mediawiki.language.js', |
Index: trunk/phase3/resources/mediawiki.special/mediawiki.special.upload.js |
— | — | @@ -58,9 +58,27 @@ |
59 | 59 | spinner.src = mw.config.get( 'wgScriptPath' ) + '/skins/common/images/spinner.gif'; |
60 | 60 | $( '#mw-htmlform-source' ).parent().prepend( thumb ); |
61 | 61 | |
62 | | - fetchPreview( file, function( dataURL ) { |
| 62 | + var meta; |
| 63 | + fetchPreview( file, function( dataURL ) { |
63 | 64 | var img = new Image(), |
64 | 65 | rotation = 0; |
| 66 | + |
| 67 | + if ( meta && meta.tiff.Orientation ) { |
| 68 | + rotation = (360 - function () { |
| 69 | + // See includes/media/Bitmap.php |
| 70 | + switch ( meta.tiff.Orientation.value ) { |
| 71 | + case 8: |
| 72 | + return 90; |
| 73 | + case 3: |
| 74 | + return 180; |
| 75 | + case 6: |
| 76 | + return 270; |
| 77 | + default: |
| 78 | + return 0; |
| 79 | + } |
| 80 | + }() ) % 360; |
| 81 | + } |
| 82 | + |
65 | 83 | img.onload = function() { |
66 | 84 | // Fit the image within the previewSizexpreviewSize box |
67 | 85 | if ( img.width > img.height ) { |
— | — | @@ -83,6 +101,7 @@ |
84 | 102 | y = dy; |
85 | 103 | break; |
86 | 104 | case 90: |
| 105 | + |
87 | 106 | x = dx; |
88 | 107 | y = dy - previewSize; |
89 | 108 | break; |
— | — | @@ -106,22 +125,44 @@ |
107 | 126 | $( '#mw-upload-thumbnail .fileinfo' ).text( info ); |
108 | 127 | }; |
109 | 128 | img.src = dataURL; |
110 | | - } ); |
| 129 | + }, mediaWiki.config.get( 'wgFileCanRotate' ) ? function ( data ) { |
| 130 | + try { |
| 131 | + meta = mediaWiki.util.jpegmeta( data, file.fileName ); |
| 132 | + meta._binary_data = null; |
| 133 | + } catch ( e ) { |
| 134 | + meta = null; |
| 135 | + } |
| 136 | + } : null ); |
111 | 137 | } |
112 | 138 | |
113 | 139 | /** |
114 | 140 | * Start loading a file into memory; when complete, pass it as a |
115 | | - * data URL to the callback function. |
| 141 | + * data URL to the callback function. If the callbackBinary is set it will |
| 142 | + * first be read as binary and afterwards as data URL. Useful if you want |
| 143 | + * to do preprocessing on the binary data first. |
116 | 144 | * |
117 | 145 | * @param {File} file |
118 | 146 | * @param {function} callback |
| 147 | + * @param {function} callbackBinary |
119 | 148 | */ |
120 | | - function fetchPreview( file, callback ) { |
| 149 | + function fetchPreview( file, callback, callbackBinary ) { |
121 | 150 | var reader = new FileReader(); |
122 | 151 | reader.onload = function() { |
123 | | - callback( reader.result ); |
| 152 | + if ( callbackBinary ) { |
| 153 | + callbackBinary( reader.result ); |
| 154 | + reader.onload = function() { |
| 155 | + callback( reader.result ); |
| 156 | + } |
| 157 | + reader.readAsDataURL( file ); |
| 158 | + } else { |
| 159 | + callback( reader.result ); |
| 160 | + } |
124 | 161 | }; |
125 | | - reader.readAsDataURL( file ); |
| 162 | + if ( callbackBinary ) { |
| 163 | + reader.readAsBinaryString( file ); |
| 164 | + } else { |
| 165 | + reader.readAsDataURL( file ); |
| 166 | + } |
126 | 167 | } |
127 | 168 | |
128 | 169 | /** |