]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/media/PNGMetadataExtractor.php
MediaWiki 1.17.0
[autoinstalls/mediawiki.git] / includes / media / PNGMetadataExtractor.php
1 <?php
2 /**
3  * PNG frame counter.
4  * Slightly derived from GIFMetadataExtractor.php
5  * Deliberately not using MWExceptions to avoid external dependencies, encouraging
6  * redistribution.
7  *
8  * @file
9  * @ingroup Media
10  */
11
12 /**
13  * PNG frame counter.
14  *
15  * @ingroup Media
16  */
17 class PNGMetadataExtractor {
18         static $png_sig;
19         static $CRC_size;
20
21         static function getMetadata( $filename ) {
22                 self::$png_sig = pack( "C8", 137, 80, 78, 71, 13, 10, 26, 10 );
23                 self::$CRC_size = 4;
24                 
25                 $frameCount = 0;
26                 $loopCount = 1;
27                 $duration = 0.0;
28
29                 if (!$filename)
30                         throw new Exception( __METHOD__ . ": No file name specified" );
31                 elseif ( !file_exists($filename) || is_dir($filename) )
32                         throw new Exception( __METHOD__ . ": File $filename does not exist" );
33                 
34                 $fh = fopen( $filename, 'r' );
35                 
36                 if (!$fh) {
37                         throw new Exception( __METHOD__ . ": Unable to open file $filename" );
38                 }
39                 
40                 // Check for the PNG header
41                 $buf = fread( $fh, 8 );
42                 if ( $buf != self::$png_sig ) {
43                         throw new Exception( __METHOD__ . ": Not a valid PNG file; header: $buf" );
44                 }
45
46                 // Read chunks
47                 while( !feof( $fh ) ) {
48                         $buf = fread( $fh, 4 );
49                         if( !$buf ) {
50                                 throw new Exception( __METHOD__ . ": Read error" );
51                         }
52                         $chunk_size = unpack( "N", $buf);
53                         $chunk_size = $chunk_size[1];
54
55                         $chunk_type = fread( $fh, 4 );
56                         if( !$chunk_type ) {
57                                 throw new Exception( __METHOD__ . ": Read error" );
58                         }
59
60                         if ( $chunk_type == "acTL" ) {
61                                 $buf = fread( $fh, $chunk_size );
62                                 if( !$buf ) {
63                                         throw new Exception( __METHOD__ . ": Read error" );
64                                 }
65
66                                 $actl = unpack( "Nframes/Nplays", $buf );
67                                 $frameCount = $actl['frames'];
68                                 $loopCount = $actl['plays'];
69                         } elseif ( $chunk_type == "fcTL" ) {
70                                 $buf = fread( $fh, $chunk_size );
71                                 if( !$buf ) {
72                                         throw new Exception( __METHOD__ . ": Read error" );
73                                 }
74                                 $buf = substr( $buf, 20 );      
75
76                                 $fctldur = unpack( "ndelay_num/ndelay_den", $buf );
77                                 if( $fctldur['delay_den'] == 0 ) $fctldur['delay_den'] = 100;
78                                 if( $fctldur['delay_num'] ) {
79                                         $duration += $fctldur['delay_num'] / $fctldur['delay_den'];
80                                 }
81                         } elseif ( ( $chunk_type == "IDAT" || $chunk_type == "IEND" ) && $frameCount == 0 ) {
82                                 // Not a valid animated image. No point in continuing.
83                                 break;
84                         } elseif ( $chunk_type == "IEND" ) {
85                                 break;
86                         } else {
87                                 fseek( $fh, $chunk_size, SEEK_CUR );
88                         }
89                         fseek( $fh, self::$CRC_size, SEEK_CUR );
90                 }
91                 fclose( $fh );
92
93                 if( $loopCount > 1 ) {
94                         $duration *= $loopCount;
95                 }
96
97                 return array(
98                         'frameCount' => $frameCount,
99                         'loopCount' => $loopCount,
100                         'duration' => $duration
101                 );
102                 
103         }
104 }