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