MediaWiki  REL1_20
FormatMetadata.php
Go to the documentation of this file.
00001 <?php
00048 class FormatMetadata {
00049 
00060         public static function getFormattedData( $tags ) {
00061                 global $wgLang;
00062 
00063                 $resolutionunit = !isset( $tags['ResolutionUnit'] ) || $tags['ResolutionUnit'] == 2 ? 2 : 3;
00064                 unset( $tags['ResolutionUnit'] );
00065 
00066                 foreach ( $tags as $tag => &$vals ) {
00067 
00068                         // This seems ugly to wrap non-array's in an array just to unwrap again,
00069                         // especially when most of the time it is not an array
00070                         if ( !is_array( $tags[$tag] ) ) {
00071                                 $vals = Array( $vals );
00072                         }
00073 
00074                         // _type is a special value to say what array type
00075                         if ( isset( $tags[$tag]['_type'] ) ) {
00076                                 $type = $tags[$tag]['_type'];
00077                                 unset( $vals['_type'] );
00078                         } else {
00079                                 $type = 'ul'; // default unordered list.
00080                         }
00081 
00082                         //This is done differently as the tag is an array.
00083                         if ($tag == 'GPSTimeStamp' && count($vals) === 3) {
00084                                 //hour min sec array
00085 
00086                                 $h = explode('/', $vals[0]);
00087                                 $m = explode('/', $vals[1]);
00088                                 $s = explode('/', $vals[2]);
00089 
00090                                 // this should already be validated
00091                                 // when loaded from file, but it could
00092                                 // come from a foreign repo, so be
00093                                 // paranoid.
00094                                 if ( !isset($h[1])
00095                                         || !isset($m[1])
00096                                         || !isset($s[1])
00097                                         || $h[1] == 0
00098                                         || $m[1] == 0
00099                                         || $s[1] == 0
00100                                 ) {
00101                                         continue;
00102                                 }
00103                                 $tags[$tag] = str_pad( intval( $h[0] / $h[1] ), 2, '0', STR_PAD_LEFT )
00104                                         . ':' . str_pad( intval( $m[0] / $m[1] ), 2, '0', STR_PAD_LEFT )
00105                                         . ':' . str_pad( intval( $s[0] / $s[1] ), 2, '0', STR_PAD_LEFT );
00106 
00107                                 try {
00108                                         $time = wfTimestamp( TS_MW, '1971:01:01 ' . $tags[$tag] );
00109                                         // the 1971:01:01 is just a placeholder, and not shown to user.
00110                                         if ( $time && intval( $time ) > 0 ) {
00111                                                 $tags[$tag] = $wgLang->time( $time );
00112                                         }
00113                                 } catch ( TimestampException $e ) {
00114                                         // This shouldn't happen, but we've seen bad formats
00115                                         // such as 4-digit seconds in the wild.
00116                                         // leave $tags[$tag] as-is
00117                                 }
00118                                 continue;
00119                         }
00120 
00121                         // The contact info is a multi-valued field
00122                         // instead of the other props which are single
00123                         // valued (mostly) so handle as a special case.
00124                         if ( $tag === 'Contact' ) {
00125                                 $vals = self::collapseContactInfo( $vals );
00126                                 continue;
00127                         }
00128 
00129                         foreach ( $vals as &$val ) {
00130 
00131                                 switch( $tag ) {
00132                                 case 'Compression':
00133                                         switch( $val ) {
00134                                         case 1: case 2: case 3: case 4:
00135                                         case 5: case 6: case 7: case 8:
00136                                         case 32773: case 32946: case 34712:
00137                                                 $val = self::msg( $tag, $val );
00138                                                 break;
00139                                         default:
00140                                                 /* If not recognized, display as is. */
00141                                                 break;
00142                                         }
00143                                         break;
00144 
00145                                 case 'PhotometricInterpretation':
00146                                         switch( $val ) {
00147                                         case 2: case 6:
00148                                                 $val = self::msg( $tag, $val );
00149                                                 break;
00150                                         default:
00151                                                 /* If not recognized, display as is. */
00152                                                 break;
00153                                         }
00154                                         break;
00155 
00156                                 case 'Orientation':
00157                                         switch( $val ) {
00158                                         case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
00159                                                 $val = self::msg( $tag, $val );
00160                                                 break;
00161                                         default:
00162                                                 /* If not recognized, display as is. */
00163                                                 break;
00164                                         }
00165                                         break;
00166 
00167                                 case 'PlanarConfiguration':
00168                                         switch( $val ) {
00169                                         case 1: case 2:
00170                                                 $val = self::msg( $tag, $val );
00171                                                 break;
00172                                         default:
00173                                                 /* If not recognized, display as is. */
00174                                                 break;
00175                                         }
00176                                         break;
00177 
00178                                 // TODO: YCbCrSubSampling
00179                                 case 'YCbCrPositioning':
00180                                         switch ( $val ) {
00181                                         case 1:
00182                                         case 2:
00183                                                 $val = self::msg( $tag, $val );
00184                                                 break;
00185                                         default:
00186                                                 /* If not recognized, display as is. */
00187                                                 break;
00188                                         }
00189                                         break;
00190 
00191                                 case 'XResolution':
00192                                 case 'YResolution':
00193                                         switch( $resolutionunit ) {
00194                                                 case 2:
00195                                                         $val = self::msg( 'XYResolution', 'i', self::formatNum( $val ) );
00196                                                         break;
00197                                                 case 3:
00198                                                         $val = self::msg( 'XYResolution', 'c', self::formatNum( $val ) );
00199                                                         break;
00200                                                 default:
00201                                                         /* If not recognized, display as is. */
00202                                                         break;
00203                                         }
00204                                         break;
00205 
00206                                 // TODO: YCbCrCoefficients  #p27 (see annex E)
00207                                 case 'ExifVersion': case 'FlashpixVersion':
00208                                         $val = "$val" / 100;
00209                                         break;
00210 
00211                                 case 'ColorSpace':
00212                                         switch( $val ) {
00213                                         case 1: case 65535:
00214                                                 $val = self::msg( $tag, $val );
00215                                                 break;
00216                                         default:
00217                                                 /* If not recognized, display as is. */
00218                                                 break;
00219                                         }
00220                                         break;
00221 
00222                                 case 'ComponentsConfiguration':
00223                                         switch( $val ) {
00224                                         case 0: case 1: case 2: case 3: case 4: case 5: case 6:
00225                                                 $val = self::msg( $tag, $val );
00226                                                 break;
00227                                         default:
00228                                                 /* If not recognized, display as is. */
00229                                                 break;
00230                                         }
00231                                         break;
00232 
00233                                 case 'DateTime':
00234                                 case 'DateTimeOriginal':
00235                                 case 'DateTimeDigitized':
00236                                 case 'DateTimeReleased':
00237                                 case 'DateTimeExpires':
00238                                 case 'GPSDateStamp':
00239                                 case 'dc-date':
00240                                 case 'DateTimeMetadata':
00241                                         if ( $val == '0000:00:00 00:00:00' || $val == '    :  :     :  :  ' ) {
00242                                                 $val = wfMessage( 'exif-unknowndate' )->text();
00243                                         } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d):(?:\d\d)$/D', $val ) ) {
00244                                                 // Full date.
00245                                                 $time = wfTimestamp( TS_MW, $val );
00246                                                 if ( $time && intval( $time ) > 0 ) {
00247                                                         $val = $wgLang->timeanddate( $time );
00248                                                 }
00249                                         } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d) (?:\d\d):(?:\d\d)$/D', $val ) ) {
00250                                                 // No second field. Still format the same
00251                                                 // since timeanddate doesn't include seconds anyways,
00252                                                 // but second still available in api
00253                                                 $time = wfTimestamp( TS_MW, $val . ':00' );
00254                                                 if ( $time && intval( $time ) > 0 ) {
00255                                                         $val = $wgLang->timeanddate( $time );
00256                                                 }
00257                                         } elseif ( preg_match( '/^(?:\d{4}):(?:\d\d):(?:\d\d)$/D', $val ) ) {
00258                                                 // If only the date but not the time is filled in.
00259                                                 $time = wfTimestamp( TS_MW, substr( $val, 0, 4 )
00260                                                         . substr( $val, 5, 2 )
00261                                                         . substr( $val, 8, 2 )
00262                                                         . '000000' );
00263                                                 if ( $time && intval( $time ) > 0 ) {
00264                                                         $val = $wgLang->date( $time );
00265                                                 }
00266                                         }
00267                                         // else it will just output $val without formatting it.
00268                                         break;
00269 
00270                                 case 'ExposureProgram':
00271                                         switch( $val ) {
00272                                         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
00273                                                 $val = self::msg( $tag, $val );
00274                                                 break;
00275                                         default:
00276                                                 /* If not recognized, display as is. */
00277                                                 break;
00278                                         }
00279                                         break;
00280 
00281                                 case 'SubjectDistance':
00282                                         $val = self::msg( $tag, '', self::formatNum( $val ) );
00283                                         break;
00284 
00285                                 case 'MeteringMode':
00286                                         switch( $val ) {
00287                                         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 255:
00288                                                 $val = self::msg( $tag, $val );
00289                                                 break;
00290                                         default:
00291                                                 /* If not recognized, display as is. */
00292                                                 break;
00293                                         }
00294                                         break;
00295 
00296                                 case 'LightSource':
00297                                         switch( $val ) {
00298                                         case 0: case 1: case 2: case 3: case 4: case 9: case 10: case 11:
00299                                         case 12: case 13: case 14: case 15: case 17: case 18: case 19: case 20:
00300                                         case 21: case 22: case 23: case 24: case 255:
00301                                                 $val = self::msg( $tag, $val );
00302                                                 break;
00303                                         default:
00304                                                 /* If not recognized, display as is. */
00305                                                 break;
00306                                         }
00307                                         break;
00308 
00309                                 case 'Flash':
00310                                         $flashDecode = array(
00311                                                 'fired'    => $val & bindec( '00000001' ),
00312                                                 'return'   => ( $val & bindec( '00000110' ) ) >> 1,
00313                                                 'mode'     => ( $val & bindec( '00011000' ) ) >> 3,
00314                                                 'function' => ( $val & bindec( '00100000' ) ) >> 5,
00315                                                 'redeye'   => ( $val & bindec( '01000000' ) ) >> 6,
00316 //                                              'reserved' => ($val & bindec( '10000000' )) >> 7,
00317                                         );
00318                                         $flashMsgs = array();
00319                                         # We do not need to handle unknown values since all are used.
00320                                         foreach ( $flashDecode as $subTag => $subValue ) {
00321                                                 # We do not need any message for zeroed values.
00322                                                 if ( $subTag != 'fired' && $subValue == 0 ) {
00323                                                         continue;
00324                                                 }
00325                                                 $fullTag = $tag . '-' . $subTag ;
00326                                                 $flashMsgs[] = self::msg( $fullTag, $subValue );
00327                                         }
00328                                         $val = $wgLang->commaList( $flashMsgs );
00329                                         break;
00330 
00331                                 case 'FocalPlaneResolutionUnit':
00332                                         switch( $val ) {
00333                                         case 2:
00334                                                 $val = self::msg( $tag, $val );
00335                                                 break;
00336                                         default:
00337                                                 /* If not recognized, display as is. */
00338                                                 break;
00339                                         }
00340                                         break;
00341 
00342                                 case 'SensingMethod':
00343                                         switch( $val ) {
00344                                         case 1: case 2: case 3: case 4: case 5: case 7: case 8:
00345                                                 $val = self::msg( $tag, $val );
00346                                                 break;
00347                                         default:
00348                                                 /* If not recognized, display as is. */
00349                                                 break;
00350                                         }
00351                                         break;
00352 
00353                                 case 'FileSource':
00354                                         switch( $val ) {
00355                                         case 3:
00356                                                 $val = self::msg( $tag, $val );
00357                                                 break;
00358                                         default:
00359                                                 /* If not recognized, display as is. */
00360                                                 break;
00361                                         }
00362                                         break;
00363 
00364                                 case 'SceneType':
00365                                         switch( $val ) {
00366                                         case 1:
00367                                                 $val = self::msg( $tag, $val );
00368                                                 break;
00369                                         default:
00370                                                 /* If not recognized, display as is. */
00371                                                 break;
00372                                         }
00373                                         break;
00374 
00375                                 case 'CustomRendered':
00376                                         switch( $val ) {
00377                                         case 0: case 1:
00378                                                 $val = self::msg( $tag, $val );
00379                                                 break;
00380                                         default:
00381                                                 /* If not recognized, display as is. */
00382                                                 break;
00383                                         }
00384                                         break;
00385 
00386                                 case 'ExposureMode':
00387                                         switch( $val ) {
00388                                         case 0: case 1: case 2:
00389                                                 $val = self::msg( $tag, $val );
00390                                                 break;
00391                                         default:
00392                                                 /* If not recognized, display as is. */
00393                                                 break;
00394                                         }
00395                                         break;
00396 
00397                                 case 'WhiteBalance':
00398                                         switch( $val ) {
00399                                         case 0: case 1:
00400                                                 $val = self::msg( $tag, $val );
00401                                                 break;
00402                                         default:
00403                                                 /* If not recognized, display as is. */
00404                                                 break;
00405                                         }
00406                                         break;
00407 
00408                                 case 'SceneCaptureType':
00409                                         switch( $val ) {
00410                                         case 0: case 1: case 2: case 3:
00411                                                 $val = self::msg( $tag, $val );
00412                                                 break;
00413                                         default:
00414                                                 /* If not recognized, display as is. */
00415                                                 break;
00416                                         }
00417                                         break;
00418 
00419                                 case 'GainControl':
00420                                         switch( $val ) {
00421                                         case 0: case 1: case 2: case 3: case 4:
00422                                                 $val = self::msg( $tag, $val );
00423                                                 break;
00424                                         default:
00425                                                 /* If not recognized, display as is. */
00426                                                 break;
00427                                         }
00428                                         break;
00429 
00430                                 case 'Contrast':
00431                                         switch( $val ) {
00432                                         case 0: case 1: case 2:
00433                                                 $val = self::msg( $tag, $val );
00434                                                 break;
00435                                         default:
00436                                                 /* If not recognized, display as is. */
00437                                                 break;
00438                                         }
00439                                         break;
00440 
00441                                 case 'Saturation':
00442                                         switch( $val ) {
00443                                         case 0: case 1: case 2:
00444                                                 $val = self::msg( $tag, $val );
00445                                                 break;
00446                                         default:
00447                                                 /* If not recognized, display as is. */
00448                                                 break;
00449                                         }
00450                                         break;
00451 
00452                                 case 'Sharpness':
00453                                         switch( $val ) {
00454                                         case 0: case 1: case 2:
00455                                                 $val = self::msg( $tag, $val );
00456                                                 break;
00457                                         default:
00458                                                 /* If not recognized, display as is. */
00459                                                 break;
00460                                         }
00461                                         break;
00462 
00463                                 case 'SubjectDistanceRange':
00464                                         switch( $val ) {
00465                                         case 0: case 1: case 2: case 3:
00466                                                 $val = self::msg( $tag, $val );
00467                                                 break;
00468                                         default:
00469                                                 /* If not recognized, display as is. */
00470                                                 break;
00471                                         }
00472                                         break;
00473 
00474                                 //The GPS...Ref values are kept for compatibility, probably won't be reached.
00475                                 case 'GPSLatitudeRef':
00476                                 case 'GPSDestLatitudeRef':
00477                                         switch( $val ) {
00478                                         case 'N': case 'S':
00479                                                 $val = self::msg( 'GPSLatitude', $val );
00480                                                 break;
00481                                         default:
00482                                                 /* If not recognized, display as is. */
00483                                                 break;
00484                                         }
00485                                         break;
00486 
00487                                 case 'GPSLongitudeRef':
00488                                 case 'GPSDestLongitudeRef':
00489                                         switch( $val ) {
00490                                         case 'E': case 'W':
00491                                                 $val = self::msg( 'GPSLongitude', $val );
00492                                                 break;
00493                                         default:
00494                                                 /* If not recognized, display as is. */
00495                                                 break;
00496                                         }
00497                                         break;
00498 
00499                                 case 'GPSAltitude':
00500                                         if ( $val < 0 ) {
00501                                                 $val = self::msg( 'GPSAltitude', 'below-sealevel', self::formatNum( -$val, 3 ) );
00502                                         } else {
00503                                                 $val = self::msg( 'GPSAltitude', 'above-sealevel', self::formatNum( $val, 3 ) );
00504                                         }
00505                                         break;
00506 
00507                                 case 'GPSStatus':
00508                                         switch( $val ) {
00509                                         case 'A': case 'V':
00510                                                 $val = self::msg( $tag, $val );
00511                                                 break;
00512                                         default:
00513                                                 /* If not recognized, display as is. */
00514                                                 break;
00515                                         }
00516                                         break;
00517 
00518                                 case 'GPSMeasureMode':
00519                                         switch( $val ) {
00520                                         case 2: case 3:
00521                                                 $val = self::msg( $tag, $val );
00522                                                 break;
00523                                         default:
00524                                                 /* If not recognized, display as is. */
00525                                                 break;
00526                                         }
00527                                         break;
00528 
00529 
00530                                 case 'GPSTrackRef':
00531                                 case 'GPSImgDirectionRef':
00532                                 case 'GPSDestBearingRef':
00533                                         switch( $val ) {
00534                                         case 'T': case 'M':
00535                                                 $val = self::msg( 'GPSDirection', $val );
00536                                                 break;
00537                                         default:
00538                                                 /* If not recognized, display as is. */
00539                                                 break;
00540                                         }
00541                                         break;
00542 
00543                                 case 'GPSLatitude':
00544                                 case 'GPSDestLatitude':
00545                                         $val = self::formatCoords( $val, 'latitude' );
00546                                         break;
00547                                 case 'GPSLongitude':
00548                                 case 'GPSDestLongitude':
00549                                         $val = self::formatCoords( $val, 'longitude' );
00550                                         break;
00551 
00552                                 case 'GPSSpeedRef':
00553                                         switch( $val ) {
00554                                         case 'K': case 'M': case 'N':
00555                                                 $val = self::msg( 'GPSSpeed', $val );
00556                                                 break;
00557                                         default:
00558                                                 /* If not recognized, display as is. */
00559                                                 break;
00560                                         }
00561                                         break;
00562 
00563                                 case 'GPSDestDistanceRef':
00564                                         switch( $val ) {
00565                                         case 'K': case 'M': case 'N':
00566                                                 $val = self::msg( 'GPSDestDistance', $val );
00567                                                 break;
00568                                         default:
00569                                                 /* If not recognized, display as is. */
00570                                                 break;
00571                                         }
00572                                         break;
00573 
00574                                 case 'GPSDOP':
00575                                         // See http://en.wikipedia.org/wiki/Dilution_of_precision_(GPS)
00576                                         if ( $val <= 2 ) {
00577                                                 $val = self::msg( $tag, 'excellent', self::formatNum( $val ) );
00578                                         } elseif ( $val <= 5 ) {
00579                                                 $val = self::msg( $tag, 'good', self::formatNum( $val ) );
00580                                         } elseif ( $val <= 10 ) {
00581                                                 $val = self::msg( $tag, 'moderate', self::formatNum( $val ) );
00582                                         } elseif ( $val <= 20 ) {
00583                                                 $val = self::msg( $tag, 'fair', self::formatNum( $val ) );
00584                                         } else {
00585                                                 $val = self::msg( $tag, 'poor', self::formatNum( $val ) );
00586                                         }
00587                                         break;
00588 
00589                                 // This is not in the Exif standard, just a special
00590                                 // case for our purposes which enables wikis to wikify
00591                                 // the make, model and software name to link to their articles.
00592                                 case 'Make':
00593                                 case 'Model':
00594                                         $val = self::msg( $tag, '', $val );
00595                                         break;
00596 
00597                                 case 'Software':
00598                                         if ( is_array( $val ) ) {
00599                                                 //if its a software, version array.
00600                                                 $val = wfMessage( 'exif-software-version-value', $val[0], $val[1] )->text();
00601                                         } else {
00602                                                 $val = self::msg( $tag, '', $val );
00603                                         }
00604                                         break;
00605 
00606                                 case 'ExposureTime':
00607                                         // Show the pretty fraction as well as decimal version
00608                                         $val = wfMessage( 'exif-exposuretime-format',
00609                                                 self::formatFraction( $val ), self::formatNum( $val ) )->text();
00610                                         break;
00611                                 case 'ISOSpeedRatings':
00612                                         // If its = 65535 that means its at the
00613                                         // limit of the size of Exif::short and
00614                                         // is really higher.
00615                                         if ( $val == '65535' ) {
00616                                                 $val = self::msg( $tag, 'overflow' );
00617                                         } else {
00618                                                 $val = self::formatNum( $val );
00619                                         }
00620                                         break;
00621                                 case 'FNumber':
00622                                         $val = wfMessage( 'exif-fnumber-format',
00623                                                 self::formatNum( $val ) )->text();
00624                                         break;
00625 
00626                                 case 'FocalLength': case 'FocalLengthIn35mmFilm':
00627                                         $val = wfMessage( 'exif-focallength-format',
00628                                                 self::formatNum( $val ) )->text();
00629                                         break;
00630 
00631                                 case 'MaxApertureValue':
00632                                         if ( strpos( $val, '/' ) !== false ) {
00633                                                 // need to expand this earlier to calculate fNumber
00634                                                 list($n, $d) = explode('/', $val);
00635                                                 if ( is_numeric( $n ) && is_numeric( $d ) ) {
00636                                                         $val = $n / $d;
00637                                                 }
00638                                         }
00639                                         if ( is_numeric( $val ) ) {
00640                                                 $fNumber = pow( 2, $val / 2 );
00641                                                 if ( $fNumber !== false ) {
00642                                                         $val = wfMessage( 'exif-maxaperturevalue-value',
00643                                                                 self::formatNum( $val ),
00644                                                                 self::formatNum( $fNumber, 2 )
00645                                                         )->text();
00646                                                 }
00647                                         }
00648                                         break;
00649 
00650                                 case 'iimCategory':
00651                                         switch( strtolower( $val ) ) {
00652                                                 // See pg 29 of IPTC photo
00653                                                 // metadata standard.
00654                                                 case 'ace': case 'clj':
00655                                                 case 'dis': case 'fin':
00656                                                 case 'edu': case 'evn':
00657                                                 case 'hth': case 'hum':
00658                                                 case 'lab': case 'lif':
00659                                                 case 'pol': case 'rel':
00660                                                 case 'sci': case 'soi':
00661                                                 case 'spo': case 'war':
00662                                                 case 'wea':
00663                                                         $val = self::msg(
00664                                                                 'iimcategory',
00665                                                                 $val
00666                                                         );
00667                                         }
00668                                         break;
00669                                 case 'SubjectNewsCode':
00670                                         // Essentially like iimCategory.
00671                                         // 8 (numeric) digit hierarchical
00672                                         // classification. We decode the
00673                                         // first 2 digits, which provide
00674                                         // a broad category.
00675                                         $val = self::convertNewsCode( $val );
00676                                         break;
00677                                 case 'Urgency':
00678                                         // 1-8 with 1 being highest, 5 normal
00679                                         // 0 is reserved, and 9 is 'user-defined'.
00680                                         $urgency = '';
00681                                         if ( $val == 0 || $val == 9 ) {
00682                                                 $urgency = 'other';
00683                                         } elseif ( $val < 5 && $val > 1 ) {
00684                                                 $urgency = 'high';
00685                                         } elseif ( $val == 5 ) {
00686                                                 $urgency = 'normal';
00687                                         } elseif ( $val <= 8 && $val > 5) {
00688                                                 $urgency = 'low';
00689                                         }
00690 
00691                                         if ( $urgency !== '' ) {
00692                                                 $val = self::msg( 'urgency',
00693                                                         $urgency, $val
00694                                                 );
00695                                         }
00696                                         break;
00697 
00698                                 // Things that have a unit of pixels.
00699                                 case 'OriginalImageHeight':
00700                                 case 'OriginalImageWidth':
00701                                 case 'PixelXDimension':
00702                                 case 'PixelYDimension':
00703                                 case 'ImageWidth':
00704                                 case 'ImageLength':
00705                                         $val = self::formatNum( $val ) . ' ' . wfMessage( 'unit-pixel' )->text();
00706                                         break;
00707 
00708                                 // Do not transform fields with pure text.
00709                                 // For some languages the formatNum()
00710                                 // conversion results to wrong output like
00711                                 // foo,bar@example,com or fooÙ«bar@exampleÙ«com.
00712                                 // Also some 'numeric' things like Scene codes
00713                                 // are included here as we really don't want
00714                                 // commas inserted.
00715                                 case 'ImageDescription':
00716                                 case 'Artist':
00717                                 case 'Copyright':
00718                                 case 'RelatedSoundFile':
00719                                 case 'ImageUniqueID':
00720                                 case 'SpectralSensitivity':
00721                                 case 'GPSSatellites':
00722                                 case 'GPSVersionID':
00723                                 case 'GPSMapDatum':
00724                                 case 'Keywords':
00725                                 case 'WorldRegionDest':
00726                                 case 'CountryDest':
00727                                 case 'CountryCodeDest':
00728                                 case 'ProvinceOrStateDest':
00729                                 case 'CityDest':
00730                                 case 'SublocationDest':
00731                                 case 'WorldRegionCreated':
00732                                 case 'CountryCreated':
00733                                 case 'CountryCodeCreated':
00734                                 case 'ProvinceOrStateCreated':
00735                                 case 'CityCreated':
00736                                 case 'SublocationCreated':
00737                                 case 'ObjectName':
00738                                 case 'SpecialInstructions':
00739                                 case 'Headline':
00740                                 case 'Credit':
00741                                 case 'Source':
00742                                 case 'EditStatus':
00743                                 case 'FixtureIdentifier':
00744                                 case 'LocationDest':
00745                                 case 'LocationDestCode':
00746                                 case 'Writer':
00747                                 case 'JPEGFileComment':
00748                                 case 'iimSupplementalCategory':
00749                                 case 'OriginalTransmissionRef':
00750                                 case 'Identifier':
00751                                 case 'dc-contributor':
00752                                 case 'dc-coverage':
00753                                 case 'dc-publisher':
00754                                 case 'dc-relation':
00755                                 case 'dc-rights':
00756                                 case 'dc-source':
00757                                 case 'dc-type':
00758                                 case 'Lens':
00759                                 case 'SerialNumber':
00760                                 case 'CameraOwnerName':
00761                                 case 'Label':
00762                                 case 'Nickname':
00763                                 case 'RightsCertificate':
00764                                 case 'CopyrightOwner':
00765                                 case 'UsageTerms':
00766                                 case 'WebStatement':
00767                                 case 'OriginalDocumentID':
00768                                 case 'LicenseUrl':
00769                                 case 'MorePermissionsUrl':
00770                                 case 'AttributionUrl':
00771                                 case 'PreferredAttributionName':
00772                                 case 'PNGFileComment':
00773                                 case 'Disclaimer':
00774                                 case 'ContentWarning':
00775                                 case 'GIFFileComment':
00776                                 case 'SceneCode':
00777                                 case 'IntellectualGenre':
00778                                 case 'Event':
00779                                 case 'OrginisationInImage':
00780                                 case 'PersonInImage':
00781 
00782                                         $val = htmlspecialchars( $val );
00783                                         break;
00784 
00785                                 case 'ObjectCycle':
00786                                         switch ( $val ) {
00787                                         case 'a': case 'p': case 'b':
00788                                                 $val = self::msg( $tag, $val );
00789                                                 break;
00790                                         default:
00791                                                 $val = htmlspecialchars( $val );
00792                                                 break;
00793                                         }
00794                                         break;
00795                                 case 'Copyrighted':
00796                                         switch( $val ) {
00797                                         case 'True': case 'False':
00798                                                 $val = self::msg( $tag, $val );
00799                                                 break;
00800                                         }
00801                                         break;
00802                                 case 'Rating':
00803                                         if ( $val == '-1' ) {
00804                                                 $val = self::msg( $tag, 'rejected' );
00805                                         } else {
00806                                                 $val = self::formatNum( $val );
00807                                         }
00808                                         break;
00809 
00810                                 case 'LanguageCode':
00811                                         $lang = Language::fetchLanguageName( strtolower( $val ), $wgLang->getCode() );
00812                                         if ($lang) {
00813                                                 $val = htmlspecialchars( $lang );
00814                                         } else {
00815                                                 $val = htmlspecialchars( $val );
00816                                         }
00817                                         break;
00818 
00819                                 default:
00820                                         $val = self::formatNum( $val );
00821                                         break;
00822                                 }
00823                         }
00824                         // End formatting values, start flattening arrays.
00825                         $vals = self::flattenArray( $vals, $type );
00826 
00827                 }
00828                 return $tags;
00829         }
00830 
00846         public static function flattenArray( $vals, $type = 'ul', $noHtml = false ) {
00847                 if ( isset( $vals['_type'] ) ) {
00848                         $type = $vals['_type'];
00849                         unset( $vals['_type'] );
00850                 }
00851 
00852                 if ( !is_array( $vals ) ) {
00853                          return $vals; // do nothing if not an array;
00854                 }
00855                 elseif ( count( $vals ) === 1 && $type !== 'lang' ) {
00856                         return $vals[0];
00857                 }
00858                 elseif ( count( $vals ) === 0 ) {
00859                         wfDebug( __METHOD__ . ' metadata array with 0 elements!' );
00860                         return ""; // paranoia. This should never happen
00861                 }
00862                 /* @todo FIXME: This should hide some of the list entries if there are
00863                  * say more than four. Especially if a field is translated into 20
00864                  * languages, we don't want to show them all by default
00865                  */
00866                 else {
00867                         global $wgContLang;
00868                         switch( $type ) {
00869                         case 'lang':
00870                                 // Display default, followed by ContLang,
00871                                 // followed by the rest in no particular
00872                                 // order.
00873 
00874                                 // Todo: hide some items if really long list.
00875 
00876                                 $content = '';
00877 
00878                                 $cLang = $wgContLang->getCode();
00879                                 $defaultItem = false;
00880                                 $defaultLang = false;
00881 
00882                                 // If default is set, save it for later,
00883                                 // as we don't know if it's equal to
00884                                 // one of the lang codes. (In xmp
00885                                 // you specify the language for a
00886                                 // default property by having both
00887                                 // a default prop, and one in the language
00888                                 // that are identical)
00889                                 if ( isset( $vals['x-default'] ) ) {
00890                                         $defaultItem = $vals['x-default'];
00891                                         unset( $vals['x-default'] );
00892                                 }
00893                                 // Do contentLanguage.
00894                                 if ( isset( $vals[$cLang] ) ) {
00895                                         $isDefault = false;
00896                                         if ( $vals[$cLang] === $defaultItem ) {
00897                                                 $defaultItem = false;
00898                                                 $isDefault = true;
00899                                         }
00900                                         $content .= self::langItem(
00901                                                 $vals[$cLang], $cLang,
00902                                                  $isDefault, $noHtml );
00903 
00904                                         unset( $vals[$cLang] );
00905                                 }
00906 
00907                                 // Now do the rest.
00908                                 foreach ( $vals as $lang => $item ) {
00909                                         if ( $item === $defaultItem ) {
00910                                                 $defaultLang = $lang;
00911                                                 continue;
00912                                         }
00913                                         $content .= self::langItem( $item,
00914                                                 $lang, false, $noHtml );
00915                                 }
00916                                 if ( $defaultItem !== false ) {
00917                                         $content = self::langItem( $defaultItem,
00918                                                 $defaultLang, true, $noHtml )
00919                                                  . $content;
00920                                 }
00921                                 if ( $noHtml ) {
00922                                         return $content;
00923                                 }
00924                                 return '<ul class="metadata-langlist">' .
00925                                         $content .
00926                                         '</ul>';
00927                         case 'ol':
00928                                 if ( $noHtml ) {
00929                                         return "\n#" . implode( "\n#", $vals );
00930                                 }
00931                                 return "<ol><li>" . implode( "</li>\n<li>", $vals ) . '</li></ol>';
00932                         case 'ul':
00933                         default:
00934                                 if ( $noHtml ) {
00935                                         return "\n*" . implode( "\n*", $vals );
00936                                 }
00937                                 return "<ul><li>" . implode( "</li>\n<li>", $vals ) . '</li></ul>';
00938                         }
00939                 }
00940         }
00941 
00952         private static function langItem( $value, $lang, $default = false, $noHtml = false ) {
00953                 if ( $lang === false && $default === false) {
00954                         throw new MWException('$lang and $default cannot both '
00955                                 . 'be false.');
00956                 }
00957 
00958                 if ( $noHtml ) {
00959                         $wrappedValue = $value;
00960                 } else {
00961                         $wrappedValue = '<span class="mw-metadata-lang-value">'
00962                                 . $value . '</span>';
00963                 }
00964 
00965                 if ( $lang === false ) {
00966                         if ( $noHtml ) {
00967                                 return wfMessage( 'metadata-langitem-default',
00968                                         $wrappedValue )->text() . "\n\n";
00969                         } /* else */
00970                         return '<li class="mw-metadata-lang-default">'
00971                                 . wfMessage( 'metadata-langitem-default',
00972                                         $wrappedValue )->text()
00973                                 . "</li>\n";
00974                 }
00975 
00976                 $lowLang = strtolower( $lang );
00977                 $langName = Language::fetchLanguageName( $lowLang );
00978                 if ( $langName === '' ) {
00979                         //try just the base language name. (aka en-US -> en ).
00980                         list( $langPrefix ) = explode( '-', $lowLang, 2 );
00981                         $langName = Language::fetchLanguageName( $langPrefix );
00982                         if ( $langName === '' ) {
00983                                 // give up.
00984                                 $langName = $lang;
00985                         }
00986                 }
00987                 // else we have a language specified
00988 
00989                 if ( $noHtml ) {
00990                         return '*' . wfMessage( 'metadata-langitem',
00991                                 $wrappedValue, $langName, $lang )->text();
00992                 } /* else: */
00993 
00994                 $item = '<li class="mw-metadata-lang-code-'
00995                         . $lang;
00996                 if ( $default ) {
00997                         $item .= ' mw-metadata-lang-default';
00998                 }
00999                 $item .= '" lang="' . $lang . '">';
01000                 $item .= wfMessage( 'metadata-langitem',
01001                         $wrappedValue, $langName, $lang )->text();
01002                 $item .= "</li>\n";
01003                 return $item;
01004         }
01005 
01017         static function msg( $tag, $val, $arg = null, $arg2 = null ) {
01018                 global $wgContLang;
01019 
01020                 if ($val === '')
01021                         $val = 'value';
01022                 return wfMessage( $wgContLang->lc( "exif-$tag-$val" ), $arg, $arg2 )->text();
01023         }
01024 
01033         static function formatNum( $num, $round = false ) {
01034                 global $wgLang;
01035                 $m = array();
01036                 if( is_array($num) ) {
01037                         $out = array();
01038                         foreach( $num as $number ) {
01039                                 $out[] = self::formatNum($number);
01040                         }
01041                         return $wgLang->commaList( $out );
01042                 }
01043                 if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
01044                         if ( $m[2] != 0 ) {
01045                                 $newNum = $m[1] / $m[2];
01046                                 if ( $round !== false ) {
01047                                         $newNum = round( $newNum, $round );
01048                                 }
01049                         } else {
01050                                 $newNum = $num;
01051                         }
01052 
01053                         return $wgLang->formatNum( $newNum );
01054                 } else {
01055                         if ( is_numeric( $num ) && $round !== false ) {
01056                                 $num = round( $num, $round );
01057                         }
01058                         return $wgLang->formatNum( $num );
01059                 }
01060         }
01061 
01070         static function formatFraction( $num ) {
01071                 $m = array();
01072                 if ( preg_match( '/^(-?\d+)\/(\d+)$/', $num, $m ) ) {
01073                         $numerator = intval( $m[1] );
01074                         $denominator = intval( $m[2] );
01075                         $gcd = self::gcd( abs( $numerator ), $denominator );
01076                         if( $gcd != 0 ) {
01077                                 // 0 shouldn't happen! ;)
01078                                 return self::formatNum( $numerator / $gcd ) . '/' . self::formatNum( $denominator / $gcd );
01079                         }
01080                 }
01081                 return self::formatNum( $num );
01082         }
01083 
01092         static function gcd( $a, $b ) {
01093                 /*
01094                         // http://en.wikipedia.org/wiki/Euclidean_algorithm
01095                         // Recursive form would be:
01096                         if( $b == 0 )
01097                                 return $a;
01098                         else
01099                                 return gcd( $b, $a % $b );
01100                 */
01101                 while( $b != 0 ) {
01102                         $remainder = $a % $b;
01103 
01104                         // tail recursion...
01105                         $a = $b;
01106                         $b = $remainder;
01107                 }
01108                 return $a;
01109         }
01110 
01123         static private function convertNewsCode( $val ) {
01124                 if ( !preg_match( '/^\d{8}$/D', $val ) ) {
01125                         // Not a valid news code.
01126                         return $val;
01127                 }
01128                 $cat = '';
01129                 switch( substr( $val , 0, 2 ) ) {
01130                         case '01':
01131                                 $cat = 'ace';
01132                                 break;
01133                         case '02':
01134                                 $cat = 'clj';
01135                                 break;
01136                         case '03':
01137                                 $cat = 'dis';
01138                                 break;
01139                         case '04':
01140                                 $cat = 'fin';
01141                                 break;
01142                         case '05':
01143                                 $cat = 'edu';
01144                                 break;
01145                         case '06':
01146                                 $cat = 'evn';
01147                                 break;
01148                         case '07':
01149                                 $cat = 'hth';
01150                                 break;
01151                         case '08':
01152                                 $cat = 'hum';
01153                                 break;
01154                         case '09':
01155                                 $cat = 'lab';
01156                                 break;
01157                         case '10':
01158                                 $cat = 'lif';
01159                                 break;
01160                         case '11':
01161                                 $cat = 'pol';
01162                                 break;
01163                         case '12':
01164                                 $cat = 'rel';
01165                                 break;
01166                         case '13':
01167                                 $cat = 'sci';
01168                                 break;
01169                         case '14':
01170                                 $cat = 'soi';
01171                                 break;
01172                         case '15':
01173                                 $cat = 'spo';
01174                                 break;
01175                         case '16':
01176                                 $cat = 'war';
01177                                 break;
01178                         case '17':
01179                                 $cat = 'wea';
01180                                 break;
01181                 }
01182                 if ( $cat !== '' ) {
01183                         $catMsg = self::msg( 'iimcategory', $cat );
01184                         $val = self::msg( 'subjectnewscode', '', $val, $catMsg );
01185                 }
01186                 return $val;
01187         }
01188 
01197         static function formatCoords( $coord, $type ) {
01198                 $ref = '';
01199                 if ( $coord < 0 ) {
01200                         $nCoord = -$coord;
01201                         if ( $type === 'latitude' ) {
01202                                 $ref = 'S';
01203                         } elseif ( $type === 'longitude' ) {
01204                                 $ref = 'W';
01205                         }
01206                 } else {
01207                         $nCoord = $coord;
01208                         if ( $type === 'latitude' ) {
01209                                 $ref = 'N';
01210                         } elseif ( $type === 'longitude' ) {
01211                                 $ref = 'E';
01212                         }
01213                 }
01214 
01215                 $deg = floor( $nCoord );
01216                 $min = floor( ( $nCoord - $deg ) * 60.0 );
01217                 $sec = round( ( ( $nCoord - $deg ) - $min / 60 ) * 3600, 2 );
01218 
01219                 $deg = self::formatNum( $deg );
01220                 $min = self::formatNum( $min );
01221                 $sec = self::formatNum( $sec );
01222 
01223                 return wfMessage( 'exif-coordinate-format', $deg, $min, $sec, $ref, $coord )->text();
01224         }
01225 
01240         public static function collapseContactInfo( $vals ) {
01241                 if( ! ( isset( $vals['CiAdrExtadr'] )
01242                         || isset( $vals['CiAdrCity'] )
01243                         || isset( $vals['CiAdrCtry'] )
01244                         || isset( $vals['CiEmailWork'] )
01245                         || isset( $vals['CiTelWork'] )
01246                         || isset( $vals['CiAdrPcode'] )
01247                         || isset( $vals['CiAdrRegion'] )
01248                         || isset( $vals['CiUrlWork'] )
01249                 ) ) {
01250                         // We don't have any sub-properties
01251                         // This could happen if its using old
01252                         // iptc that just had this as a free-form
01253                         // text value.
01254                         // Note: We run this through htmlspecialchars
01255                         // partially to be consistent, and partially
01256                         // because people often insert >, etc into
01257                         // the metadata which should not be interpreted
01258                         // but we still want to auto-link urls.
01259                         foreach( $vals as &$val ) {
01260                                 $val = htmlspecialchars( $val );
01261                         }
01262                         return self::flattenArray( $vals );
01263                 } else {
01264                         // We have a real ContactInfo field.
01265                         // Its unclear if all these fields have to be
01266                         // set, so assume they do not.
01267                         $url = $tel = $street = $city = $country = '';
01268                         $email = $postal = $region = '';
01269 
01270                         // Also note, some of the class names this uses
01271                         // are similar to those used by hCard. This is
01272                         // mostly because they're sensible names. This
01273                         // does not (and does not attempt to) output
01274                         // stuff in the hCard microformat. However it
01275                         // might output in the adr microformat.
01276 
01277                         if ( isset( $vals['CiAdrExtadr'] ) ) {
01278                                 // Todo: This can potentially be multi-line.
01279                                 // Need to check how that works in XMP.
01280                                 $street = '<span class="extended-address">'
01281                                         . htmlspecialchars(
01282                                                 $vals['CiAdrExtadr'] )
01283                                         . '</span>';
01284                         }
01285                         if ( isset( $vals['CiAdrCity'] ) ) {
01286                                 $city = '<span class="locality">'
01287                                         . htmlspecialchars( $vals['CiAdrCity'] )
01288                                         . '</span>';
01289                         }
01290                         if ( isset( $vals['CiAdrCtry'] ) ) {
01291                                 $country = '<span class="country-name">'
01292                                         . htmlspecialchars( $vals['CiAdrCtry'] )
01293                                         . '</span>';
01294                         }
01295                         if ( isset( $vals['CiEmailWork'] ) ) {
01296                                 $emails = array();
01297                                 // Have to split multiple emails at commas/new lines.
01298                                 $splitEmails = explode( "\n", $vals['CiEmailWork'] );
01299                                 foreach ( $splitEmails as $e1 ) {
01300                                         // Also split on comma
01301                                         foreach ( explode( ',', $e1 ) as $e2 ) {
01302                                                 $finalEmail = trim( $e2 );
01303                                                 if ( $finalEmail == ',' || $finalEmail == '' ) {
01304                                                         continue;
01305                                                 }
01306                                                 if ( strpos( $finalEmail, '<' ) !== false ) {
01307                                                         // Don't do fancy formatting to
01308                                                         // "My name" <[email protected]> style stuff
01309                                                         $emails[] = $finalEmail;
01310                                                 } else {
01311                                                         $emails[] = '[mailto:'
01312                                                         . $finalEmail
01313                                                         . ' <span class="email">'
01314                                                         . $finalEmail
01315                                                         . '</span>]';
01316                                                 }
01317                                         }
01318                                 }
01319                                 $email = implode( ', ', $emails );
01320                         }
01321                         if ( isset( $vals['CiTelWork'] ) ) {
01322                                 $tel = '<span class="tel">'
01323                                         . htmlspecialchars( $vals['CiTelWork'] )
01324                                         . '</span>';
01325                         }
01326                         if ( isset( $vals['CiAdrPcode'] ) ) {
01327                                 $postal = '<span class="postal-code">'
01328                                         . htmlspecialchars(
01329                                                 $vals['CiAdrPcode'] )
01330                                         . '</span>';
01331                         }
01332                         if ( isset( $vals['CiAdrRegion'] ) ) {
01333                                 // Note this is province/state.
01334                                 $region = '<span class="region">'
01335                                         . htmlspecialchars(
01336                                                 $vals['CiAdrRegion'] )
01337                                         . '</span>';
01338                         }
01339                         if ( isset( $vals['CiUrlWork'] ) ) {
01340                                 $url = '<span class="url">'
01341                                         . htmlspecialchars( $vals['CiUrlWork'] )
01342                                         . '</span>';
01343                         }
01344                         return wfMessage( 'exif-contact-value', $email, $url,
01345                                 $street, $city, $region, $postal, $country,
01346                                 $tel )->text();
01347                 }
01348         }
01349 }
01350 
01357 class FormatExif {
01358         var $meta;
01359 
01363         function FormatExif( $meta ) {
01364                 wfDeprecated(__METHOD__);
01365                 $this->meta = $meta;
01366         }
01367 
01371         function getFormattedData() {
01372                 return FormatMetadata::getFormattedData( $this->meta );
01373         }
01374 }