4 * Slightly derived from GIFMetadataExtractor.php
5 * Deliberately not using MWExceptions to avoid external dependencies, encouraging
17 class PNGMetadataExtractor
{
21 static function getMetadata( $filename ) {
22 self
::$png_sig = pack( "C8", 137, 80, 78, 71, 13, 10, 26, 10 );
29 $colorType = 'unknown';
32 throw new Exception( __METHOD__
. ": No file name specified" );
33 elseif ( !file_exists($filename) ||
is_dir($filename) )
34 throw new Exception( __METHOD__
. ": File $filename does not exist" );
36 $fh = fopen( $filename, 'r' );
39 throw new Exception( __METHOD__
. ": Unable to open file $filename" );
42 // Check for the PNG header
43 $buf = fread( $fh, 8 );
44 if ( $buf != self
::$png_sig ) {
45 throw new Exception( __METHOD__
. ": Not a valid PNG file; header: $buf" );
49 while( !feof( $fh ) ) {
50 $buf = fread( $fh, 4 );
52 throw new Exception( __METHOD__
. ": Read error" );
54 $chunk_size = unpack( "N", $buf);
55 $chunk_size = $chunk_size[1];
57 $chunk_type = fread( $fh, 4 );
59 throw new Exception( __METHOD__
. ": Read error" );
62 if ( $chunk_type == "IHDR" ) {
63 $buf = fread( $fh, $chunk_size );
65 throw new Exception( __METHOD__
. ": Read error" );
67 $bitDepth = ord( substr( $buf, 8, 1 ) );
68 // Detect the color type in British English as per the spec
69 // http://www.w3.org/TR/PNG/#11IHDR
70 switch ( ord( substr( $buf, 9, 1 ) ) ) {
72 $colorType = 'greyscale';
75 $colorType = 'truecolour';
78 $colorType = 'index-coloured';
81 $colorType = 'greyscale-alpha';
84 $colorType = 'truecolour-alpha';
87 $colorType = 'unknown';
90 } elseif ( $chunk_type == "acTL" ) {
91 $buf = fread( $fh, $chunk_size );
93 throw new Exception( __METHOD__
. ": Read error" );
96 $actl = unpack( "Nframes/Nplays", $buf );
97 $frameCount = $actl['frames'];
98 $loopCount = $actl['plays'];
99 } elseif ( $chunk_type == "fcTL" ) {
100 $buf = fread( $fh, $chunk_size );
102 throw new Exception( __METHOD__
. ": Read error" );
104 $buf = substr( $buf, 20 );
106 $fctldur = unpack( "ndelay_num/ndelay_den", $buf );
107 if( $fctldur['delay_den'] == 0 ) $fctldur['delay_den'] = 100;
108 if( $fctldur['delay_num'] ) {
109 $duration +
= $fctldur['delay_num'] / $fctldur['delay_den'];
111 } elseif ( ( $chunk_type == "IDAT" ||
$chunk_type == "IEND" ) && $frameCount == 0 ) {
112 // Not a valid animated image. No point in continuing.
114 } elseif ( $chunk_type == "IEND" ) {
117 fseek( $fh, $chunk_size, SEEK_CUR
);
119 fseek( $fh, self
::$CRC_size, SEEK_CUR
);
123 if( $loopCount > 1 ) {
124 $duration *= $loopCount;
128 'frameCount' => $frameCount,
129 'loopCount' => $loopCount,
130 'duration' => $duration,
131 'bitDepth' => $bitDepth,
132 'colorType' => $colorType,