PNGHandler.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <?php
  2. /**
  3. * Handler for PNG images.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Media
  22. */
  23. /**
  24. * Handler for PNG images.
  25. *
  26. * @ingroup Media
  27. */
  28. class PNGHandler extends BitmapHandler {
  29. const BROKEN_FILE = '0';
  30. /**
  31. * @param File|FSFile $image
  32. * @param string $filename
  33. * @return string
  34. */
  35. public function getMetadata( $image, $filename ) {
  36. try {
  37. $metadata = BitmapMetadataHandler::PNG( $filename );
  38. } catch ( Exception $e ) {
  39. // Broken file?
  40. wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" );
  41. return self::BROKEN_FILE;
  42. }
  43. return serialize( $metadata );
  44. }
  45. /**
  46. * @param File $image
  47. * @param bool|IContextSource $context Context to use (optional)
  48. * @return array|bool
  49. */
  50. public function formatMetadata( $image, $context = false ) {
  51. $meta = $this->getCommonMetaArray( $image );
  52. if ( count( $meta ) === 0 ) {
  53. return false;
  54. }
  55. return $this->formatMetadataHelper( $meta, $context );
  56. }
  57. /**
  58. * Get a file type independent array of metadata.
  59. *
  60. * @param File $image
  61. * @return array The metadata array
  62. */
  63. public function getCommonMetaArray( File $image ) {
  64. $meta = $image->getMetadata();
  65. if ( !$meta ) {
  66. return [];
  67. }
  68. $meta = unserialize( $meta );
  69. if ( !isset( $meta['metadata'] ) ) {
  70. return [];
  71. }
  72. unset( $meta['metadata']['_MW_PNG_VERSION'] );
  73. return $meta['metadata'];
  74. }
  75. /**
  76. * @param File $image
  77. * @return bool
  78. */
  79. function isAnimatedImage( $image ) {
  80. $ser = $image->getMetadata();
  81. if ( $ser ) {
  82. $metadata = unserialize( $ser );
  83. if ( $metadata['frameCount'] > 1 ) {
  84. return true;
  85. }
  86. }
  87. return false;
  88. }
  89. /**
  90. * We do not support making APNG thumbnails, so always false
  91. * @param File $image
  92. * @return bool False
  93. */
  94. function canAnimateThumbnail( $image ) {
  95. return false;
  96. }
  97. function getMetadataType( $image ) {
  98. return 'parsed-png';
  99. }
  100. public function isMetadataValid( $image, $metadata ) {
  101. if ( $metadata === self::BROKEN_FILE ) {
  102. // Do not repetitivly regenerate metadata on broken file.
  103. return self::METADATA_GOOD;
  104. }
  105. Wikimedia\suppressWarnings();
  106. $data = unserialize( $metadata );
  107. Wikimedia\restoreWarnings();
  108. if ( !$data || !is_array( $data ) ) {
  109. wfDebug( __METHOD__ . " invalid png metadata\n" );
  110. return self::METADATA_BAD;
  111. }
  112. if ( !isset( $data['metadata']['_MW_PNG_VERSION'] )
  113. || $data['metadata']['_MW_PNG_VERSION'] != PNGMetadataExtractor::VERSION
  114. ) {
  115. wfDebug( __METHOD__ . " old but compatible png metadata\n" );
  116. return self::METADATA_COMPATIBLE;
  117. }
  118. return self::METADATA_GOOD;
  119. }
  120. /**
  121. * @param File $image
  122. * @return string
  123. */
  124. public function getLongDesc( $image ) {
  125. global $wgLang;
  126. $original = parent::getLongDesc( $image );
  127. Wikimedia\suppressWarnings();
  128. $metadata = unserialize( $image->getMetadata() );
  129. Wikimedia\restoreWarnings();
  130. if ( !$metadata || $metadata['frameCount'] <= 0 ) {
  131. return $original;
  132. }
  133. $info = [];
  134. $info[] = $original;
  135. if ( $metadata['loopCount'] == 0 ) {
  136. $info[] = wfMessage( 'file-info-png-looped' )->parse();
  137. } elseif ( $metadata['loopCount'] > 1 ) {
  138. $info[] = wfMessage( 'file-info-png-repeat' )->numParams( $metadata['loopCount'] )->parse();
  139. }
  140. if ( $metadata['frameCount'] > 0 ) {
  141. $info[] = wfMessage( 'file-info-png-frames' )->numParams( $metadata['frameCount'] )->parse();
  142. }
  143. if ( $metadata['duration'] ) {
  144. $info[] = $wgLang->formatTimePeriod( $metadata['duration'] );
  145. }
  146. return $wgLang->commaList( $info );
  147. }
  148. /**
  149. * Return the duration of an APNG file.
  150. *
  151. * Shown in the &query=imageinfo&iiprop=size api query.
  152. *
  153. * @param File $file
  154. * @return float The duration of the file.
  155. */
  156. public function getLength( $file ) {
  157. $serMeta = $file->getMetadata();
  158. Wikimedia\suppressWarnings();
  159. $metadata = unserialize( $serMeta );
  160. Wikimedia\restoreWarnings();
  161. if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) {
  162. return 0.0;
  163. } else {
  164. return (float)$metadata['duration'];
  165. }
  166. }
  167. // PNGs should be easy to support, but it will need some sharpening applied
  168. // and another user test to check if the perceived quality change is noticeable
  169. public function supportsBucketing() {
  170. return false;
  171. }
  172. }