r71097 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r71096‎ | r71097 | r71098 >
Date:06:31, 15 August 2010
Author:bawolff
Status:ok
Tags:
Comment:
follow-up to r71096 . Forgot to do svn add...
Modified paths:
  • /branches/img_metadata/phase3/includes/media/FormatMetadata.php (added) (history)

Diff [purge]

Index: branches/img_metadata/phase3/includes/media/FormatMetadata.php
@@ -0,0 +1,916 @@
 2+<?php
 3+/**
 4+ * This program is free software; you can redistribute it and/or modify
 5+ * it under the terms of the GNU General Public License as published by
 6+ * the Free Software Foundation; either version 2 of the License, or
 7+ * (at your option) any later version.
 8+ *
 9+ * This program is distributed in the hope that it will be useful,
 10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12+ * GNU General Public License for more details.
 13+ *
 14+ * You should have received a copy of the GNU General Public License along
 15+ * with this program; if not, write to the Free Software Foundation, Inc.,
 16+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 17+ * http://www.gnu.org/copyleft/gpl.html
 18+ *
 19+ * @ingroup Media
 20+ * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
 21+ * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason, 2009 Brent Garber
 22+ * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
 23+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 specification
 24+ * @file
 25+ */
 26+
 27+
 28+/**
 29+ * Format Image metadata values into a human readable form.
 30+ *
 31+ * Note lots of these messages use the prefix 'exif' even though
 32+ * they may not be exif properties. For example 'exif-ImageDescription'
 33+ * can be the Exif ImageDescription, or it could be the iptc-iim caption
 34+ * property, or it could be the xmp dc:description property. This
 35+ * is because these messages should be independent of how the data is
 36+ * stored, sine the user doesn't care if the description is stored in xmp,
 37+ * exif, etc only that its a description. (Additionally many of these properties
 38+ * are merged together following the MWG standard, such that for example,
 39+ * exif properties override XMP properties that mean the same thing if
 40+ * there is a conflict).
 41+ *
 42+ * It should perhaps use a prefix like 'metadata' instead, but there
 43+ * is already a large number of messages using the 'exif' prefix.
 44+ *
 45+ * @ingroup Media
 46+ */
 47+class FormatMetadata {
 48+
 49+ /**
 50+ * Numbers given by Exif user agents are often magical, that is they
 51+ * should be replaced by a detailed explanation depending on their
 52+ * value which most of the time are plain integers. This function
 53+ * formats Exif (and other metadata) values into human readable form.
 54+ *
 55+ * @param $tags Array: the Exif data to format ( as returned by
 56+ * Exif::getFilteredData() or BitmapMetadataHandler )
 57+ * @return array
 58+ */
 59+ public static function getFormattedData( $tags ) {
 60+ global $wgLang;
 61+
 62+ $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3;
 63+ unset( $tags['ResolutionUnit'] );
 64+
 65+ foreach ( $tags as $tag => &$vals ) {
 66+
 67+ // This seems ugly to wrap non-array's in an array just to unwrap again,
 68+ // especially when most of the time it is not an array
 69+ if ( !is_array( $tags[$tag] ) ) {
 70+ $vals = Array( $vals );
 71+ }
 72+
 73+ // _type is a special value to say what array type
 74+ if ( isset( $tags[$tag]['_type'] ) ) {
 75+ $type = $tags[$tag]['_type'];
 76+ unset( $vals['_type'] );
 77+ } else {
 78+ $type = 'ul'; // default unorcdered list.
 79+ }
 80+
 81+ //This is done differently as the tag is an array.
 82+ if ($tag == 'GPSTimeStamp' && count($vals) === 3) {
 83+ //hour min sec array
 84+
 85+ $h = explode('/', $vals[0]);
 86+ $m = explode('/', $vals[1]);
 87+ $s = explode('/', $vals[2]);
 88+
 89+ // this should already be validated
 90+ // when loaded from file, but it could
 91+ // come from a foreign repo, so be
 92+ // paranoid.
 93+ if ( !isset($h[1])
 94+ || !isset($m[1])
 95+ || !isset($s[1])
 96+ || $h[1] == 0
 97+ || $m[1] == 0
 98+ || $s[1] == 0
 99+ ) {
 100+ continue;
 101+ }
 102+ $tags[$tag] = intval( $h[0] / $h[1] )
 103+ . ':' . intval( $m[0] / $m[1] )
 104+ . ':' . str_pad( intval( $s[0] / $s[1] ), 2, '0', STR_PAD_LEFT );
 105+ continue;
 106+ }
 107+
 108+
 109+ foreach ( $vals as &$val ) {
 110+
 111+ switch( $tag ) {
 112+ case 'Compression':
 113+ switch( $val ) {
 114+ case 1: case 6:
 115+ $val = self::msg( $tag, $val );
 116+ break;
 117+ default:
 118+ $val = $val;
 119+ break;
 120+ }
 121+ break;
 122+
 123+ case 'PhotometricInterpretation':
 124+ switch( $val ) {
 125+ case 2: case 6:
 126+ $val = self::msg( $tag, $val );
 127+ break;
 128+ default:
 129+ $val = $val;
 130+ break;
 131+ }
 132+ break;
 133+
 134+ case 'Orientation':
 135+ switch( $val ) {
 136+ case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
 137+ $val = self::msg( $tag, $val );
 138+ break;
 139+ default:
 140+ $val = $val;
 141+ break;
 142+ }
 143+ break;
 144+
 145+ case 'PlanarConfiguration':
 146+ switch( $val ) {
 147+ case 1: case 2:
 148+ $val = self::msg( $tag, $val );
 149+ break;
 150+ default:
 151+ $val = $val;
 152+ break;
 153+ }
 154+ break;
 155+
 156+ // TODO: YCbCrSubSampling
 157+ case 'YCbCrPositioning':
 158+ switch ( $val ) {
 159+ case 1:
 160+ case 2:
 161+ $val = self::msg( $tag, $val );
 162+ break;
 163+ default:
 164+ $val = $val;
 165+ break;
 166+ }
 167+ break;
 168+
 169+ case 'XResolution':
 170+ case 'YResolution':
 171+ switch( $resolutionunit ) {
 172+ case 2:
 173+ $val = self::msg( 'XYResolution', 'i', self::formatNum( $val ) );
 174+ break;
 175+ case 3:
 176+ $val = self::msg( 'XYResolution', 'c', self::formatNum( $val ) );
 177+ break;
 178+ default:
 179+ $val = $val;
 180+ break;
 181+ }
 182+ break;
 183+
 184+ // TODO: YCbCrCoefficients #p27 (see annex E)
 185+ case 'ExifVersion': case 'FlashpixVersion':
 186+ $val = "$val" / 100;
 187+ break;
 188+
 189+ case 'ColorSpace':
 190+ switch( $val ) {
 191+ case 1: case 'FFFF.H':
 192+ $val = self::msg( $tag, $val );
 193+ break;
 194+ default:
 195+ $val = $val;
 196+ break;
 197+ }
 198+ break;
 199+
 200+ case 'ComponentsConfiguration':
 201+ switch( $val ) {
 202+ case 0: case 1: case 2: case 3: case 4: case 5: case 6:
 203+ $val = self::msg( $tag, $val );
 204+ break;
 205+ default:
 206+ $val = $val;
 207+ break;
 208+ }
 209+ break;
 210+
 211+ case 'DateTime':
 212+ case 'DateTimeOriginal':
 213+ case 'DateTimeDigitized':
 214+ case 'DateTimeReleased':
 215+ case 'DateTimeExpires':
 216+ case 'GPSDateStamp':
 217+ case 'dc-date':
 218+ case 'DateTimeMetadata':
 219+ if ( $val == '0000:00:00 00:00:00' || $val == ' : : : : ' ) {
 220+ $val = wfMsg( 'exif-unknowndate' );
 221+ } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/', $val ) ) {
 222+ $val = $wgLang->timeanddate( wfTimestamp( TS_MW, $val ) );
 223+ } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d)$/', $val ) ) {
 224+ // avoid using wfTimestamp here for the pre-1902 photos
 225+ // due to reverse y2k38 bug. $wgLang->timeanddate() is also
 226+ // broken on dates from before 1902 so don't worry about it
 227+ // in the above case (not to mention that most photos from the
 228+ // 1800's don't have a time recorded anyways).
 229+ $val = $wgLang->date( substr( $val, 0, 4 )
 230+ . substr( $val, 5, 2 )
 231+ . substr( $val, 8, 2 )
 232+ . '000000' );
 233+ }
 234+ // else it will just output $val without formatting it.
 235+ break;
 236+
 237+ case 'ExposureProgram':
 238+ switch( $val ) {
 239+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
 240+ $val = self::msg( $tag, $val );
 241+ break;
 242+ default:
 243+ $val = $val;
 244+ break;
 245+ }
 246+ break;
 247+
 248+ case 'SubjectDistance':
 249+ $val = self::msg( $tag, '', self::formatNum( $val ) );
 250+ break;
 251+
 252+ case 'MeteringMode':
 253+ switch( $val ) {
 254+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255:
 255+ $val = self::msg( $tag, $val );
 256+ break;
 257+ default:
 258+ $val = $val;
 259+ break;
 260+ }
 261+ break;
 262+
 263+ case 'LightSource':
 264+ switch( $val ) {
 265+ case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11:
 266+ case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20:
 267+ case 21: case 22: case 23: case 24: case 255:
 268+ $val = self::msg( $tag, $val );
 269+ break;
 270+ default:
 271+ $val = $val;
 272+ break;
 273+ }
 274+ break;
 275+
 276+ case 'Flash':
 277+ $flashDecode = array(
 278+ 'fired' => $val & bindec( '00000001' ),
 279+ 'return' => ( $val & bindec( '00000110' ) ) >> 1,
 280+ 'mode' => ( $val & bindec( '00011000' ) ) >> 3,
 281+ 'function' => ( $val & bindec( '00100000' ) ) >> 5,
 282+ 'redeye' => ( $val & bindec( '01000000' ) ) >> 6,
 283+// 'reserved' => ($val & bindec( '10000000' )) >> 7,
 284+ );
 285+
 286+ # We do not need to handle unknown values since all are used.
 287+ foreach ( $flashDecode as $subTag => $subValue ) {
 288+ # We do not need any message for zeroed values.
 289+ if ( $subTag != 'fired' && $subValue == 0 ) {
 290+ continue;
 291+ }
 292+ $fullTag = $tag . '-' . $subTag ;
 293+ $flashMsgs[] = self::msg( $fullTag, $subValue );
 294+ }
 295+ $val = $wgLang->commaList( $flashMsgs );
 296+ break;
 297+
 298+ case 'FocalPlaneResolutionUnit':
 299+ switch( $val ) {
 300+ case 2:
 301+ $val = self::msg( $tag, $val );
 302+ break;
 303+ default:
 304+ $val = $val;
 305+ break;
 306+ }
 307+ break;
 308+
 309+ case 'SensingMethod':
 310+ switch( $val ) {
 311+ case 1: case 2: case 3: case 4: case 5: case 7: case 8:
 312+ $val = self::msg( $tag, $val );
 313+ break;
 314+ default:
 315+ $val = $val;
 316+ break;
 317+ }
 318+ break;
 319+
 320+ case 'FileSource':
 321+ switch( $val ) {
 322+ case 3:
 323+ $val = self::msg( $tag, $val );
 324+ break;
 325+ default:
 326+ $val = $val;
 327+ break;
 328+ }
 329+ break;
 330+
 331+ case 'SceneType':
 332+ switch( $val ) {
 333+ case 1:
 334+ $val = self::msg( $tag, $val );
 335+ break;
 336+ default:
 337+ $val = $val;
 338+ break;
 339+ }
 340+ break;
 341+
 342+ case 'CustomRendered':
 343+ switch( $val ) {
 344+ case 0: case 1:
 345+ $val = self::msg( $tag, $val );
 346+ break;
 347+ default:
 348+ $val = $val;
 349+ break;
 350+ }
 351+ break;
 352+
 353+ case 'ExposureMode':
 354+ switch( $val ) {
 355+ case 0: case 1: case 2:
 356+ $val = self::msg( $tag, $val );
 357+ break;
 358+ default:
 359+ $val = $val;
 360+ break;
 361+ }
 362+ break;
 363+
 364+ case 'WhiteBalance':
 365+ switch( $val ) {
 366+ case 0: case 1:
 367+ $val = self::msg( $tag, $val );
 368+ break;
 369+ default:
 370+ $val = $val;
 371+ break;
 372+ }
 373+ break;
 374+
 375+ case 'SceneCaptureType':
 376+ switch( $val ) {
 377+ case 0: case 1: case 2: case 3:
 378+ $val = self::msg( $tag, $val );
 379+ break;
 380+ default:
 381+ $val = $val;
 382+ break;
 383+ }
 384+ break;
 385+
 386+ case 'GainControl':
 387+ switch( $val ) {
 388+ case 0: case 1: case 2: case 3: case 4:
 389+ $val = self::msg( $tag, $val );
 390+ break;
 391+ default:
 392+ $val = $val;
 393+ break;
 394+ }
 395+ break;
 396+
 397+ case 'Contrast':
 398+ switch( $val ) {
 399+ case 0: case 1: case 2:
 400+ $val = self::msg( $tag, $val );
 401+ break;
 402+ default:
 403+ $val = $val;
 404+ break;
 405+ }
 406+ break;
 407+
 408+ case 'Saturation':
 409+ switch( $val ) {
 410+ case 0: case 1: case 2:
 411+ $val = self::msg( $tag, $val );
 412+ break;
 413+ default:
 414+ $val = $val;
 415+ break;
 416+ }
 417+ break;
 418+
 419+ case 'Sharpness':
 420+ switch( $val ) {
 421+ case 0: case 1: case 2:
 422+ $val = self::msg( $tag, $val );
 423+ break;
 424+ default:
 425+ $val = $val;
 426+ break;
 427+ }
 428+ break;
 429+
 430+ case 'SubjectDistanceRange':
 431+ switch( $val ) {
 432+ case 0: case 1: case 2: case 3:
 433+ $val = self::msg( $tag, $val );
 434+ break;
 435+ default:
 436+ $val = $val;
 437+ break;
 438+ }
 439+ break;
 440+
 441+ //The GPS...Ref values are kept for compatability, probably won't be reached.
 442+ case 'GPSLatitudeRef':
 443+ case 'GPSDestLatitudeRef':
 444+ switch( $val ) {
 445+ case 'N': case 'S':
 446+ $val = self::msg( 'GPSLatitude', $val );
 447+ break;
 448+ default:
 449+ $val = $val;
 450+ break;
 451+ }
 452+ break;
 453+
 454+ case 'GPSLongitudeRef':
 455+ case 'GPSDestLongitudeRef':
 456+ switch( $val ) {
 457+ case 'E': case 'W':
 458+ $val = self::msg( 'GPSLongitude', $val );
 459+ break;
 460+ default:
 461+ $val = $val;
 462+ break;
 463+ }
 464+ break;
 465+
 466+ case 'GPSAltitude':
 467+ if ( $val < 0 ) {
 468+ $val = self::msg( 'GPSAltitude', 'below-sealevel', self::formatNum( -$val ) );
 469+ } else {
 470+ $val = self::msg( 'GPSAltitude', 'above-sealevel', self::formatNum( $val ) );
 471+ }
 472+ break;
 473+
 474+ case 'GPSStatus':
 475+ switch( $val ) {
 476+ case 'A': case 'V':
 477+ $val = self::msg( $tag, $val );
 478+ break;
 479+ default:
 480+ $val = $val;
 481+ break;
 482+ }
 483+ break;
 484+
 485+ case 'GPSMeasureMode':
 486+ switch( $val ) {
 487+ case 2: case 3:
 488+ $val = self::msg( $tag, $val );
 489+ break;
 490+ default:
 491+ $val = $val;
 492+ break;
 493+ }
 494+ break;
 495+
 496+
 497+ case 'GPSTrackRef':
 498+ case 'GPSImgDirectionRef':
 499+ case 'GPSDestBearingRef':
 500+ switch( $val ) {
 501+ case 'T': case 'M':
 502+ $val = self::msg( 'GPSDirection', $val );
 503+ break;
 504+ default:
 505+ $val = $val;
 506+ break;
 507+ }
 508+ break;
 509+
 510+ case 'GPSLatitude':
 511+ case 'GPSDestLatitude':
 512+ $val = self::formatCoords( $val, 'latitude' );
 513+ break;
 514+ case 'GPSLongitude':
 515+ case 'GPSDestLongitude':
 516+ $val = self::formatCoords( $val, 'longitude' );
 517+ break;
 518+
 519+ case 'GPSSpeedRef':
 520+ switch( $val ) {
 521+ case 'K': case 'M': case 'N':
 522+ $val = self::msg( 'GPSSpeed', $val );
 523+ break;
 524+ default:
 525+ $val = $val;
 526+ break;
 527+ }
 528+ break;
 529+
 530+ case 'GPSDestDistanceRef':
 531+ switch( $val ) {
 532+ case 'K': case 'M': case 'N':
 533+ $val = self::msg( 'GPSDestDistance', $val );
 534+ break;
 535+ default:
 536+ $val = $val;
 537+ break;
 538+ }
 539+ break;
 540+
 541+ case 'GPSDOP':
 542+ // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
 543+ if ( $val <= 2 ) {
 544+ $val = self::msg( $tag, 'excellent', self::formatNum( $val ) );
 545+ } elseif ( $val <= 5 ) {
 546+ $val = self::msg( $tag, 'good', self::formatNum( $val ) );
 547+ } elseif ( $val <= 10 ) {
 548+ $val = self::msg( $tag, 'moderate', self::formatNum( $val ) );
 549+ } elseif ( $val <= 20 ) {
 550+ $val = self::msg( $tag, 'fair', self::formatNum( $val ) );
 551+ } else {
 552+ $val = self::msg( $tag, 'poor', self::formatNum( $val ) );
 553+ }
 554+ break;
 555+
 556+
 557+
 558+ // This is not in the Exif standard, just a special
 559+ // case for our purposes which enables wikis to wikify
 560+ // the make, model and software name to link to their articles.
 561+ case 'Make':
 562+ case 'Model':
 563+ $val = self::msg( $tag, '', $val );
 564+ break;
 565+
 566+ case 'Software':
 567+ if ( is_array( $val ) ) {
 568+ //if its a software, version array.
 569+ $val = wfMsg( 'exif-software-version-value', $val[0], $val[1] );
 570+ } else {
 571+ $val = self::msg( $tag, '', $val );
 572+ }
 573+ break;
 574+
 575+ case 'ExposureTime':
 576+ // Show the pretty fraction as well as decimal version
 577+ $val = wfMsg( 'exif-exposuretime-format',
 578+ self::formatFraction( $val ), self::formatNum( $val ) );
 579+ break;
 580+ case 'ISOSpeedRatings':
 581+ // If its = 65535 that means its at the
 582+ // limit of the size of Exif::short and
 583+ // is really higher.
 584+ if ( $val == '65535' ) {
 585+ $val = self::msg( $tag, 'overflow' );
 586+ } else {
 587+ $val = self::formatNum( $val );
 588+ }
 589+ break;
 590+ case 'FNumber':
 591+ $val = wfMsg( 'exif-fnumber-format',
 592+ self::formatNum( $val ) );
 593+ break;
 594+
 595+ case 'FocalLength':
 596+ $val = wfMsg( 'exif-focallength-format',
 597+ self::formatNum( $val ) );
 598+ break;
 599+
 600+ // Do not transform fields with pure text.
 601+ // For some languages the formatNum() conversion results to wrong output like
 602+ // foo,bar@example,com or foo٫bar@example٫com
 603+ case 'ImageDescription':
 604+ case 'Artist':
 605+ case 'Copyright':
 606+ case 'RelatedSoundFile':
 607+ case 'ImageUniqueID':
 608+ case 'SpectralSensitivity':
 609+ case 'GPSSatellites':
 610+ case 'GPSVersionID':
 611+ case 'GPSMapDatum':
 612+ case 'Keywords':
 613+ case 'CountryDest':
 614+ case 'CountryDestCode':
 615+ case 'ProvinceOrStateDest':
 616+ case 'CityDest':
 617+ case 'SublocationDest':
 618+ case 'ObjectName':
 619+ case 'SpecialInstructions':
 620+ case 'Headline':
 621+ case 'Credit':
 622+ case 'Source':
 623+ case 'EditStatus':
 624+ case 'Urgency':
 625+ case 'FixtureIdentifier':
 626+ case 'LocationDest':
 627+ case 'LocationDestCode':
 628+ case 'Contact':
 629+ case 'Writer':
 630+ case 'JPEGFileComment':
 631+ case 'iimCategory':
 632+ case 'iimSupplementalCategory':
 633+ case 'OriginalTransmissionRef':
 634+ case 'Identifier':
 635+ case 'dc-contributor':
 636+ case 'dc-coverage':
 637+ case 'dc-publisher':
 638+ case 'dc-relation':
 639+ case 'dc-rights':
 640+ case 'dc-source':
 641+ case 'dc-type':
 642+ case 'Lens':
 643+ case 'SerialNumber':
 644+ case 'CameraOwnerName':
 645+ case 'Label':
 646+ case 'Nickname':
 647+ case 'RightsCertificate':
 648+ case 'CopyrightOwner':
 649+ case 'UsageTerms':
 650+ case 'WebStatement':
 651+ case 'OriginalDocumentID':
 652+ case 'LicenseUrl':
 653+ case 'MorePermissionsUrl':
 654+ case 'AttributionUrl':
 655+ case 'PreferredAttributionName':
 656+
 657+ $val = htmlspecialchars( $val );
 658+ break;
 659+
 660+ case 'ObjectCycle':
 661+ switch ( $val ) {
 662+ case 'a': case 'p': case 'b':
 663+ $val = self::msg( $tag, $val );
 664+ break;
 665+ default:
 666+ $val = htmlspecialchars( $val );
 667+ break;
 668+ }
 669+ break;
 670+ case 'Copyrighted':
 671+ switch( $val ) {
 672+ case 'True': case 'False':
 673+ $val = self::msg( $tag, $val );
 674+ break;
 675+ }
 676+ break;
 677+ case 'Rating':
 678+ if ( $val == '-1' ) {
 679+ $val = self::msg( $tag, 'rejected' );
 680+ } else {
 681+ $val = self::formatNum( $val );
 682+ }
 683+ break;
 684+
 685+ case 'LanguageCode':
 686+ $lang = $wgLang->getLanguageName( strtolower( $val ) );
 687+ if ($lang) {
 688+ $val = htmlspecialchars( $lang );
 689+ } else {
 690+ $val = htmlspecialchars( $val );
 691+ }
 692+ break;
 693+
 694+ default:
 695+ $val = self::formatNum( $val );
 696+ break;
 697+ }
 698+ }
 699+ // End formatting values, start flattening arrays.
 700+ $vals = self::flattenArray( $vals, $type );
 701+
 702+ }
 703+ return $tags;
 704+ }
 705+
 706+ /**
 707+ * A function to collapse multivalued tags into a single value.
 708+ * This turns an array of (for example) authors into a bulleted list.
 709+ *
 710+ * This is public on the basis it might be useful outside of this class.
 711+ *
 712+ * @param $vals Array array of values
 713+ * @param $type Type of array (either lang, ul, ol).
 714+ * lang = language assoc array with keys being the lang code
 715+ * ul = unorded list, ol = ordered list
 716+ * type can also come from the '_type' member of $vals.
 717+ * @return String single value (in wiki-syntax).
 718+ */
 719+ public static function flattenArray( $vals, $type = 'ul' ) {
 720+
 721+ if ( isset( $vals['_type'] ) ) {
 722+ $type = $vals['_type'];
 723+ }
 724+
 725+ if ( !is_array( $vals ) ) {
 726+ return $vals; // do nothing if not an array;
 727+ }
 728+ elseif ( count( $vals ) === 1 && $type !== 'lang' ) {
 729+ return $vals[0];
 730+ }
 731+ elseif ( count( $vals ) === 0 ) {
 732+ return ""; // paranoia. This should never happen
 733+ wfDebug( __METHOD__ . ' metadata array with 0 elements!' );
 734+ }
 735+ /* Fixme: This should hide some of the list entries if there are
 736+ * say more than four. Especially if a field is translated into 20
 737+ * languages, we don't want to show them all by default
 738+ */
 739+ else {
 740+ switch( $type ) {
 741+ case 'lang':
 742+ // fixme incomplete
 743+ // should place x-default, content language, user language
 744+ // first. then the others, hidden by defualt.
 745+ // also should use much better markup.
 746+ $content = "";
 747+ if ( $vals['x-default'] ) {
 748+ $content .= "\n*" . $vals['x-default'];
 749+ unset( $vals['x-default'] );
 750+ }
 751+ foreach ( $vals as $lang => $item ) {
 752+ global $wgContLang;
 753+ $content .= "\n*<span lang=\"$lang\">"
 754+ . "'''$lang''' $item</span>";
 755+ }
 756+ return $content;
 757+ case 'ol':
 758+ return "<ol><li>" . implode( "</li>\n<li>", $vals ) . '</li></ol>';
 759+ case 'ul':
 760+ default:
 761+ return "<ul><li>" . implode( "</li>\n<li>", $vals ) . '</li></ul>';
 762+ }
 763+ }
 764+ }
 765+ /**
 766+ * Convenience function for getFormattedData()
 767+ *
 768+ * @private
 769+ *
 770+ * @param $tag String: the tag name to pass on
 771+ * @param $val String: the value of the tag
 772+ * @param $arg String: an argument to pass ($1)
 773+ * @return string A wfMsg of "exif-$tag-$val" in lower case
 774+ */
 775+ static function msg( $tag, $val, $arg = null ) {
 776+ global $wgContLang;
 777+
 778+ if ($val === '')
 779+ $val = 'value';
 780+ return wfMsg( $wgContLang->lc( "exif-$tag-$val" ), $arg );
 781+ }
 782+
 783+ /**
 784+ * Format a number, convert numbers from fractions into floating point
 785+ * numbers, joins arrays of numbers with commas.
 786+ *
 787+ * @private
 788+ *
 789+ * @param $num Mixed: the value to format
 790+ * @return mixed A floating point number or whatever we were fed
 791+ */
 792+ static function formatNum( $num ) {
 793+ global $wgLang;
 794+ $m = array();
 795+ if( is_array($num) ) {
 796+ $out = array();
 797+ foreach( $num as $number ) {
 798+ $out[] = self::formatNum($number);
 799+ }
 800+ return $wgLang->commaList( $out );
 801+ }
 802+ if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) )
 803+ return $wgLang->formatNum( $m[2] != 0 ? $m[1] / $m[2] : $num );
 804+ else
 805+ return $wgLang->formatNum( $num );
 806+ }
 807+
 808+ /**
 809+ * Format a rational number, reducing fractions
 810+ *
 811+ * @private
 812+ *
 813+ * @param $num Mixed: the value to format
 814+ * @return mixed A floating point number or whatever we were fed
 815+ */
 816+ static function formatFraction( $num ) {
 817+ $m = array();
 818+ if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
 819+ $numerator = intval( $m[1] );
 820+ $denominator = intval( $m[2] );
 821+ $gcd = self::gcd( abs( $numerator ), $denominator );
 822+ if( $gcd != 0 ) {
 823+ // 0 shouldn't happen! ;)
 824+ return self::formatNum( $numerator / $gcd ) . '/' . self::formatNum( $denominator / $gcd );
 825+ }
 826+ }
 827+ return self::formatNum( $num );
 828+ }
 829+
 830+ /**
 831+ * Calculate the greatest common divisor of two integers.
 832+ *
 833+ * @param $a Integer: Numerator
 834+ * @param $b Integer: Denominator
 835+ * @return int
 836+ * @private
 837+ */
 838+ static function gcd( $a, $b ) {
 839+ /*
 840+ // http://en.wikipedia.org/wiki/Euclidean_algorithm
 841+ // Recursive form would be:
 842+ if( $b == 0 )
 843+ return $a;
 844+ else
 845+ return gcd( $b, $a % $b );
 846+ */
 847+ while( $b != 0 ) {
 848+ $remainder = $a % $b;
 849+
 850+ // tail recursion...
 851+ $a = $b;
 852+ $b = $remainder;
 853+ }
 854+ return $a;
 855+ }
 856+
 857+ /**
 858+ * Format a coordinate value, convert numbers from floating point
 859+ * into degree minute second representation.
 860+ *
 861+ * @private
 862+ *
 863+ * @param $coords Array: degrees, minutes and seconds
 864+ * @param $type String: latitude or longitude (for if its a NWS or E)
 865+ * @return mixed A floating point number or whatever we were fed
 866+ */
 867+ static function formatCoords( $coord, $type ) {
 868+ $ref = '';
 869+ if ( $coord < 0 ) {
 870+ $nCoord = -$coord;
 871+ if ( $type === 'latitude' ) {
 872+ $ref = 'S';
 873+ }
 874+ elseif ( $type === 'longitude' ) {
 875+ $ref = 'W';
 876+ }
 877+ }
 878+ else {
 879+ $nCoord = $coord;
 880+ if ( $type === 'latitude' ) {
 881+ $ref = 'N';
 882+ }
 883+ elseif ( $type === 'longitude' ) {
 884+ $ref = 'E';
 885+ }
 886+ }
 887+
 888+ $deg = floor( $nCoord );
 889+ $min = floor( ( $nCoord - $deg ) * 60.0 );
 890+ $sec = round( ( ( $nCoord - $deg ) - $min / 60 ) * 3600, 2 );
 891+
 892+ $deg = self::formatNum( $deg );
 893+ $min = self::formatNum( $min );
 894+ $sec = self::formatNum( $sec );
 895+
 896+ return wfMsg( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord );
 897+ }
 898+
 899+}
 900+
 901+/** For compatability with old FormatExif class
 902+ * which some extensions use.
 903+ *
 904+ *@deprected
 905+ *
 906+**/
 907+class FormatExif {
 908+ var $meta;
 909+ function FormatExif ( $meta ) {
 910+ wfDeprecated(__METHOD__);
 911+ $this->meta = $meta;
 912+ }
 913+ function getFormattedData ( ) {
 914+ return FormatMetadata::getFormattedData( $this->meta );
 915+ }
 916+
 917+}
Property changes on: branches/img_metadata/phase3/includes/media/FormatMetadata.php
___________________________________________________________________
Added: svn:eol-style
1918 + native

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r71096* Split the non-exif stuff in Exif.php to its own file...bawolff04:13, 15 August 2010

Status & tagging log