r68324 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r68323‎ | r68324 | r68325 >
Date:16:09, 20 June 2010
Author:hartman
Status:ok (Comments)
Tags:
Comment:
Add a new PNG parser in order to recognize APNG (animated PNG) images.
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/DefaultSettings.php (modified) (history)
  • /trunk/phase3/includes/media/PNG.php (added) (history)
  • /trunk/phase3/includes/media/PNGMetadataExtractor.php (added) (history)
  • /trunk/phase3/includes/mime.types (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesQqq.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/language/messages.inc
@@ -2510,6 +2510,9 @@
25112511 'show-big-image-thumb',
25122512 'file-info-gif-looped',
25132513 'file-info-gif-frames',
 2514+ 'file-info-png-looped',
 2515+ 'file-info-png-repeat',
 2516+ 'file-info-png-frames',
25142517 ),
25152518 'newfiles' => array(
25162519 'newimages',
Index: trunk/phase3/includes/media/PNGMetadataExtractor.php
@@ -0,0 +1,87 @@
 2+<?php
 3+/**
 4+ * PNG frame counter.
 5+ * Based on
 6+ * Deliberately not using MWExceptions to avoid external dependencies, encouraging
 7+ * redistribution.
 8+ */
 9+
 10+class PNGMetadataExtractor {
 11+ static $png_sig;
 12+ static $CRC_size;
 13+
 14+ static function getMetadata( $filename ) {
 15+ self::$png_sig = pack( "C8", 137, 80, 78, 71, 13, 10, 26, 10 );
 16+ self::$CRC_size = 4;
 17+
 18+ $frameCount = 0;
 19+ $loopCount = 1;
 20+ $duration = 0.0;
 21+
 22+ if (!$filename)
 23+ throw new Exception( __METHOD__ . "No file name specified" );
 24+ elseif ( !file_exists($filename) || is_dir($filename) )
 25+ throw new Exception( __METHOD__ . "File $filename does not exist" );
 26+
 27+ $fh = fopen( $filename, 'r' );
 28+
 29+ if (!$fh)
 30+ throw new Exception( __METHOD__ . "Unable to open file $filename" );
 31+
 32+ // Check for the PNG header
 33+ $buf = fread( $fh, 8 );
 34+ if ( !($buf == self::$png_sig) ) {
 35+ throw new Exception( __METHOD__ . "Not a valid PNG file; header: $buf" );
 36+ }
 37+
 38+ // Read chunks
 39+ while( !feof( $fh ) ) {
 40+ $buf = fread( $fh, 4 );
 41+ if( !$buf ) { throw new Exception( __METHOD__ . "Read error" ); return; }
 42+ $chunk_size = unpack( "N", $buf);
 43+ $chunk_size = $chunk_size[1];
 44+
 45+ $chunk_type = fread( $fh, 4 );
 46+ if( !$chunk_type ) { throw new Exception( __METHOD__ . "Read error" ); return; }
 47+
 48+ if ( $chunk_type == "acTL" ) {
 49+ $buf = fread( $fh, $chunk_size );
 50+ if( !$buf ) { throw new Exception( __METHOD__ . "Read error" ); return; }
 51+
 52+ $actl = unpack( "Nframes/Nplays", $buf );
 53+ $frameCount = $actl['frames'];
 54+ $loopCount = $actl['plays'];
 55+ } elseif ( $chunk_type == "fcTL" ) {
 56+ $buf = fread( $fh, $chunk_size );
 57+ if( !$buf ) { throw new Exception( __METHOD__ . "Read error" ); return; }
 58+ $buf = substr( $buf, 20 );
 59+
 60+ $fctldur = unpack( "ndelay_num/ndelay_den", $buf );
 61+ if( $fctldur['delay_den'] == 0 ) $fctldur['delay_den'] = 100;
 62+ if( $fctldur['delay_num'] ) {
 63+ $duration += $fctldur['delay_num'] / $fctldur['delay_den'];
 64+ }
 65+ } elseif ( ( $chunk_type == "IDAT" || $chunk_type == "IEND" ) && $frameCount == 0 ) {
 66+ // Not a valid animated image. No point in continuing.
 67+ break;
 68+ } elseif ( $chunk_type == "IEND" ) {
 69+ break;
 70+ } else {
 71+ fseek( $fh, $chunk_size, SEEK_CUR );
 72+ }
 73+ fseek( $fh, self::$CRC_size, SEEK_CUR );
 74+ }
 75+ fclose( $fh );
 76+
 77+ if( $loopCount > 1 ) {
 78+ $duration *= $loopCount;
 79+ }
 80+
 81+ return array(
 82+ 'frameCount' => $frameCount,
 83+ 'loopCount' => $loopCount,
 84+ 'duration' => $duration
 85+ );
 86+
 87+ }
 88+}
Property changes on: trunk/phase3/includes/media/PNGMetadataExtractor.php
___________________________________________________________________
Name: svn:eol-style
189 + native
Index: trunk/phase3/includes/media/PNG.php
@@ -0,0 +1,82 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup Media
 6+ */
 7+
 8+/**
 9+ * Handler for PNG images.
 10+ *
 11+ * @ingroup Media
 12+ */
 13+class PNGHandler extends BitmapHandler {
 14+
 15+ function getMetadata( $image, $filename ) {
 16+ if ( !isset($image->parsedPNGMetadata) ) {
 17+ try {
 18+ $image->parsedPNGMetadata = PNGMetadataExtractor::getMetadata( $filename );
 19+ } catch( Exception $e ) {
 20+ // Broken file?
 21+ wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );
 22+ return '0';
 23+ }
 24+ }
 25+
 26+ return serialize($image->parsedPNGMetadata);
 27+
 28+ }
 29+
 30+ function formatMetadata( $image ) {
 31+ return false;
 32+ }
 33+
 34+ function isAnimatedImage( $image ) {
 35+ $ser = $image->getMetadata();
 36+ if ($ser) {
 37+ $metadata = unserialize($ser);
 38+ if( $metadata['frameCount'] > 1 ) return true;
 39+ }
 40+ return false;
 41+ }
 42+
 43+ function getMetadataType( $image ) {
 44+ return 'parsed-png';
 45+ }
 46+
 47+ function isMetadataValid( $image, $metadata ) {
 48+ wfSuppressWarnings();
 49+ $data = unserialize( $metadata );
 50+ wfRestoreWarnings();
 51+ return (boolean) $data;
 52+ }
 53+ function getLongDesc( $image ) {
 54+ global $wgUser, $wgLang;
 55+ $sk = $wgUser->getSkin();
 56+ $original = parent::getLongDesc( $image );
 57+
 58+ wfSuppressWarnings();
 59+ $metadata = unserialize($image->getMetadata());
 60+ wfRestoreWarnings();
 61+
 62+ if( !metadata || $metadata['frameCount'] == 0 )
 63+ return $original;
 64+
 65+ $info[] = substr( $original, 1, strlen( $original )-2 );
 66+
 67+ if ($metadata['loopCount'] == 0)
 68+ $info[] = wfMsgExt( 'file-info-png-looped', 'parseinline' );
 69+ elseif ($metadata['loopCount'] > 1)
 70+ $info[] = wfMsgExt( 'file-info-png-repeat', 'parseinline', $metadata['loopCount'] );;
 71+
 72+ if ($metadata['frameCount'] > 0)
 73+ $info[] = wfMsgExt( 'file-info-png-frames', 'parseinline', $metadata['frameCount'] );
 74+
 75+ if ($metadata['duration'])
 76+ $info[] = $wgLang->formatTimePeriod( $metadata['duration'] );
 77+
 78+ $infoString = $wgLang->commaList( $info );
 79+
 80+ return "($infoString)";
 81+ }
 82+
 83+}
Property changes on: trunk/phase3/includes/media/PNG.php
___________________________________________________________________
Name: svn:eol-style
184 + native
Index: trunk/phase3/includes/AutoLoader.php
@@ -444,6 +444,8 @@
445445 'MediaHandler' => 'includes/media/Generic.php',
446446 'MediaTransformError' => 'includes/media/MediaTransformOutput.php',
447447 'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php',
 448+ 'PNGHandler' => 'includes/media/PNG.php',
 449+ 'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php',
448450 'SvgHandler' => 'includes/media/SVG.php',
449451 'ThumbnailImage' => 'includes/media/MediaTransformOutput.php',
450452 'TiffHandler' => 'includes/media/Tiff.php',
Index: trunk/phase3/includes/mime.types
@@ -78,7 +78,7 @@
7979 image/gif gif
8080 image/ief ief
8181 image/jpeg jpeg jpg jpe
82 -image/png png
 82+image/png png apng
8383 image/svg+xml svg
8484 image/tiff tiff tif
8585 image/vnd.djvu djvu djv
Index: trunk/phase3/includes/DefaultSettings.php
@@ -562,7 +562,7 @@
563563 */
564564 $wgMediaHandlers = array(
565565 'image/jpeg' => 'BitmapHandler',
566 - 'image/png' => 'BitmapHandler',
 566+ 'image/png' => 'PNGHandler',
567567 'image/gif' => 'GIFHandler',
568568 'image/tiff' => 'TiffHandler',
569569 'image/x-ms-bmp' => 'BmpHandler',
Index: trunk/phase3/languages/messages/MessagesQqq.php
@@ -2933,6 +2933,9 @@
29342934 'show-big-image-thumb' => 'File info displayed on file description page.',
29352935 'file-info-gif-looped' => 'Part of the information provided about a [http://en.wikipedia.org/wiki/Gif .gif file] on its file description page. Looped means repeating in the context of an animated gif. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop. For example of message in use see [[:File:Mouse10.gif]].',
29362936 'file-info-gif-frames' => 'Part of the information provided about a [http://en.wikipedia.org/wiki/Gif .gif file] on its file description page.
 2937+'file-info-png-looped' => 'Part of the information provided about a [http://en.wikipedia.org/wiki/APNG .apng file] on its file description page. Looped means repeating indefinetly in the context of an animated png. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop.',
 2938+'file-info-png-repeat' => 'Part of the information provided about a [http://en.wikipedia.org/wiki/APNG .apng file] on its file description page. The sequence of images is repeating a limited amount of time. It is a sequence of images, each displayed after the other, and the first one displayed after the last, for $1 times.',
 2939+'file-info-png-frames' => 'Part of the information provided about a [http://en.wikipedia.org/wiki/APNG .apng file] on its file description page.
29372940
29382941 The variable $1 is the number of individual frames in an animated gif file.
29392942
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -3552,6 +3552,9 @@
35533553 'show-big-image-thumb' => '<small>Size of this preview: $1 × $2 pixels</small>',
35543554 'file-info-gif-looped' => 'looped',
35553555 'file-info-gif-frames' => '$1 {{PLURAL:$1|frame|frames}}',
 3556+'file-info-png-looped' => 'looped',
 3557+'file-info-png-repeat' => 'played $1 times',
 3558+'file-info-png-frames' => '$1 {{PLURAL:$1|frame|frames}}',
35563559
35573560 # Special:NewFiles
35583561 'newimages' => 'Gallery of new files',

Follow-up revisions

RevisionCommit summaryAuthorDate
r68327Follow up r68324....hartman16:15, 20 June 2010
r68328Follow up to r68324. Fix undefined constant.hartman16:17, 20 June 2010
r68329Follow-up r68324: Fix syntax errorraymond16:35, 20 June 2010
r68348Follow-up r68324: Add usage of PLURAL to message 'file-info-png-repeat'. Code...siebrand11:15, 21 June 2010
r70105Cleanup debug and comments of r68324hartman19:38, 28 July 2010
r79964Move the () surrounding description strings of files, out of the description ...hartman22:18, 10 January 2011

Comments

#Comment by Nikerabbit (talk | contribs)   16:18, 20 June 2010
+  * Based on 

on what?

+  * Deliberately not using MWExceptions to avoid external dependencies, encouraging

You could still make your own error class to distinguish other errors.

Maybe add author and license tags too?

+				if( !$buf ) { throw new Exception( __METHOD__ . "Read error" ); return; }

You probably want something like ": " after __METHOD__


#Comment by TheDJ (talk | contribs)   16:19, 20 June 2010

Actually, that's from GIFMetadataExtractor.php

#Comment by TheDJ (talk | contribs)   22:31, 20 June 2010

I'm not sure what to do with all this. I don't really think that this needs any encouragement for redistribution as GIFMetadataExtractor, since PNG is a rather simple format. Perhaps it is better to take out all that stuff. Setting this as fixme, so that I won't forget about it.

#Comment by MaxSem (talk | contribs)   16:27, 20 June 2010

MediaWiki's reaction to exceptions other than MWexception or its descendants is ugly: instead of nice skinned "Internal error" you see a plaintext "Unexpected non-MediaWiki exception encountered, of type foo". Not nice.

#Comment by Nikerabbit (talk | contribs)   16:29, 20 June 2010

Not a problem since the exception will be caught.

#Comment by Bryan (talk | contribs)   19:21, 2 February 2011

Is ok.

Status & tagging log