MediaWiki  REL1_22
BitmapMetadataHandler.php
Go to the documentation of this file.
00001 <?php
00034 class BitmapMetadataHandler {
00035 
00036     private $metadata = array();
00037     private $metaPriority = array(
00038         20 => array( 'other' ),
00039         40 => array( 'native' ),
00040         60 => array( 'iptc-good-hash', 'iptc-no-hash' ),
00041         70 => array( 'xmp-deprecated' ),
00042         80 => array( 'xmp-general' ),
00043         90 => array( 'xmp-exif' ),
00044         100 => array( 'iptc-bad-hash' ),
00045         120 => array( 'exif' ),
00046     );
00047     private $iptcType = 'iptc-no-hash';
00048 
00057     private function doApp13( $app13 ) {
00058         try {
00059             $this->iptcType = JpegMetadataExtractor::doPSIR( $app13 );
00060         } catch ( MWException $e ) {
00061             // Error reading the iptc hash information.
00062             // This probably means the App13 segment is something other than what we expect.
00063             // However, still try to read it, and treat it as if the hash didn't exist.
00064             wfDebug( "Error parsing iptc data of file: " . $e->getMessage() . "\n" );
00065             $this->iptcType = 'iptc-no-hash';
00066         }
00067 
00068         $iptc = IPTC::parse( $app13 );
00069         $this->addMetadata( $iptc, $this->iptcType );
00070     }
00071 
00082     function getExif( $filename, $byteOrder ) {
00083         global $wgShowEXIF;
00084         if ( file_exists( $filename ) && $wgShowEXIF ) {
00085             $exif = new Exif( $filename, $byteOrder );
00086             $data = $exif->getFilteredData();
00087             if ( $data ) {
00088                 $this->addMetadata( $data, 'exif' );
00089             }
00090         }
00091     }
00098     function addMetadata( $metaArray, $type = 'other' ) {
00099         if ( isset( $this->metadata[$type] ) ) {
00100             /* merge with old data */
00101             $metaArray = $metaArray + $this->metadata[$type];
00102         }
00103 
00104         $this->metadata[$type] = $metaArray;
00105     }
00106 
00116     function getMetadataArray() {
00117         // this seems a bit ugly... This is all so its merged in right order
00118         // based on the MWG recomendation.
00119         $temp = Array();
00120         krsort( $this->metaPriority );
00121         foreach ( $this->metaPriority as $pri ) {
00122             foreach ( $pri as $type ) {
00123                 if ( isset( $this->metadata[$type] ) ) {
00124                     // Do some special casing for multilingual values.
00125                     // Don't discard translations if also as a simple value.
00126                     foreach ( $this->metadata[$type] as $itemName => $item ) {
00127                         if ( is_array( $item ) && isset( $item['_type'] ) && $item['_type'] === 'lang' ) {
00128                             if ( isset( $temp[$itemName] ) && !is_array( $temp[$itemName] ) ) {
00129                                 $default = $temp[$itemName];
00130                                 $temp[$itemName] = $item;
00131                                 $temp[$itemName]['x-default'] = $default;
00132                                 unset( $this->metadata[$type][$itemName] );
00133                             }
00134                         }
00135                     }
00136 
00137                     $temp = $temp + $this->metadata[$type];
00138                 }
00139             }
00140         }
00141         return $temp;
00142     }
00143 
00150     static function Jpeg( $filename ) {
00151         $showXMP = function_exists( 'xml_parser_create_ns' );
00152         $meta = new self();
00153 
00154         $seg = JpegMetadataExtractor::segmentSplitter( $filename );
00155         if ( isset( $seg['COM'] ) && isset( $seg['COM'][0] ) ) {
00156             $meta->addMetadata( Array( 'JPEGFileComment' => $seg['COM'] ), 'native' );
00157         }
00158         if ( isset( $seg['PSIR'] ) && count( $seg['PSIR'] ) > 0 ) {
00159             foreach ( $seg['PSIR'] as $curPSIRValue ) {
00160                 $meta->doApp13( $curPSIRValue );
00161             }
00162         }
00163         if ( isset( $seg['XMP'] ) && $showXMP ) {
00164             $xmp = new XMPReader();
00165             $xmp->parse( $seg['XMP'] );
00166             foreach ( $seg['XMP_ext'] as $xmpExt ) {
00167                 /* Support for extended xmp in jpeg files
00168                  * is not well tested and a bit fragile.
00169                  */
00170                 $xmp->parseExtended( $xmpExt );
00171 
00172             }
00173             $res = $xmp->getResults();
00174             foreach ( $res as $type => $array ) {
00175                 $meta->addMetadata( $array, $type );
00176             }
00177         }
00178         if ( isset( $seg['byteOrder'] ) ) {
00179             $meta->getExif( $filename, $seg['byteOrder'] );
00180         }
00181         return $meta->getMetadataArray();
00182     }
00183 
00192     public static function PNG( $filename ) {
00193         $showXMP = function_exists( 'xml_parser_create_ns' );
00194 
00195         $meta = new self();
00196         $array = PNGMetadataExtractor::getMetadata( $filename );
00197         if ( isset( $array['text']['xmp']['x-default'] ) && $array['text']['xmp']['x-default'] !== '' && $showXMP ) {
00198             $xmp = new XMPReader();
00199             $xmp->parse( $array['text']['xmp']['x-default'] );
00200             $xmpRes = $xmp->getResults();
00201             foreach ( $xmpRes as $type => $xmpSection ) {
00202                 $meta->addMetadata( $xmpSection, $type );
00203             }
00204         }
00205         unset( $array['text']['xmp'] );
00206         $meta->addMetadata( $array['text'], 'native' );
00207         unset( $array['text'] );
00208         $array['metadata'] = $meta->getMetadataArray();
00209         $array['metadata']['_MW_PNG_VERSION'] = PNGMetadataExtractor::VERSION;
00210         return $array;
00211     }
00212 
00221     public static function GIF( $filename ) {
00222 
00223         $meta = new self();
00224         $baseArray = GIFMetadataExtractor::getMetadata( $filename );
00225 
00226         if ( count( $baseArray['comment'] ) > 0 ) {
00227             $meta->addMetadata( array( 'GIFFileComment' => $baseArray['comment'] ), 'native' );
00228         }
00229 
00230         if ( $baseArray['xmp'] !== '' && function_exists( 'xml_parser_create_ns' ) ) {
00231             $xmp = new XMPReader();
00232             $xmp->parse( $baseArray['xmp'] );
00233             $xmpRes = $xmp->getResults();
00234             foreach ( $xmpRes as $type => $xmpSection ) {
00235                 $meta->addMetadata( $xmpSection, $type );
00236             }
00237 
00238         }
00239 
00240         unset( $baseArray['comment'] );
00241         unset( $baseArray['xmp'] );
00242 
00243         $baseArray['metadata'] = $meta->getMetadataArray();
00244         $baseArray['metadata']['_MW_GIF_VERSION'] = GIFMetadataExtractor::VERSION;
00245         return $baseArray;
00246     }
00247 
00262     public static function Tiff( $filename ) {
00263         if ( file_exists( $filename ) ) {
00264             $byteOrder = self::getTiffByteOrder( $filename );
00265             if ( !$byteOrder ) {
00266                 throw new MWException( "Error determining byte order of $filename" );
00267             }
00268             $exif = new Exif( $filename, $byteOrder );
00269             $data = $exif->getFilteredData();
00270             if ( $data ) {
00271                 $data['MEDIAWIKI_EXIF_VERSION'] = Exif::version();
00272                 return $data;
00273             } else {
00274                 throw new MWException( "Could not extract data from tiff file $filename" );
00275             }
00276         } else {
00277             throw new MWException( "File doesn't exist - $filename" );
00278         }
00279     }
00287     static function getTiffByteOrder( $filename ) {
00288         $fh = fopen( $filename, 'rb' );
00289         if ( !$fh ) {
00290             return false;
00291         }
00292         $head = fread( $fh, 2 );
00293         fclose( $fh );
00294 
00295         switch ( $head ) {
00296             case 'II':
00297                 return 'LE'; // II for intel.
00298             case 'MM':
00299                 return 'BE'; // MM for motorla.
00300             default:
00301                 return false; // Something went wrong.
00302 
00303         }
00304     }
00305 }