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/** 25 * Handler for PNG images. 26 * 27 * @ingroup Media 28 */ 29class PNGHandler extends BitmapHandler { 30 private const BROKEN_FILE = '0'; 31 32 /** 33 * @param File|FSFile $image 34 * @param string $filename 35 * @return string 36 */ 37 public function getMetadata( $image, $filename ) { 38 try { 39 $metadata = BitmapMetadataHandler::PNG( $filename ); 40 } catch ( Exception $e ) { 41 // Broken file? 42 wfDebug( __METHOD__ . ': ' . $e->getMessage() ); 43 44 return self::BROKEN_FILE; 45 } 46 47 return serialize( $metadata ); 48 } 49 50 /** 51 * @param File $image 52 * @param IContextSource|false $context 53 * @return array[]|false 54 */ 55 public function formatMetadata( $image, $context = false ) { 56 $meta = $this->getCommonMetaArray( $image ); 57 if ( !$meta ) { 58 return false; 59 } 60 61 return $this->formatMetadataHelper( $meta, $context ); 62 } 63 64 /** 65 * Get a file type independent array of metadata. 66 * 67 * @param File $image 68 * @return array The metadata array 69 */ 70 public function getCommonMetaArray( File $image ) { 71 $meta = $image->getMetadata(); 72 73 if ( !$meta ) { 74 return []; 75 } 76 $meta = unserialize( $meta ); 77 if ( !isset( $meta['metadata'] ) ) { 78 return []; 79 } 80 unset( $meta['metadata']['_MW_PNG_VERSION'] ); 81 82 return $meta['metadata']; 83 } 84 85 /** 86 * @param File $image 87 * @return bool 88 */ 89 public function isAnimatedImage( $image ) { 90 $ser = $image->getMetadata(); 91 if ( $ser ) { 92 $metadata = unserialize( $ser ); 93 if ( $metadata['frameCount'] > 1 ) { 94 return true; 95 } 96 } 97 98 return false; 99 } 100 101 /** 102 * We do not support making APNG thumbnails, so always false 103 * @param File $image 104 * @return bool False 105 */ 106 public function canAnimateThumbnail( $image ) { 107 return false; 108 } 109 110 public function getMetadataType( $image ) { 111 return 'parsed-png'; 112 } 113 114 public function isMetadataValid( $image, $metadata ) { 115 if ( $metadata === self::BROKEN_FILE ) { 116 // Do not repetitivly regenerate metadata on broken file. 117 return self::METADATA_GOOD; 118 } 119 120 Wikimedia\suppressWarnings(); 121 $data = unserialize( $metadata ); 122 Wikimedia\restoreWarnings(); 123 124 if ( !$data || !is_array( $data ) ) { 125 wfDebug( __METHOD__ . " invalid png metadata" ); 126 127 return self::METADATA_BAD; 128 } 129 130 if ( !isset( $data['metadata']['_MW_PNG_VERSION'] ) 131 || $data['metadata']['_MW_PNG_VERSION'] != PNGMetadataExtractor::VERSION 132 ) { 133 wfDebug( __METHOD__ . " old but compatible png metadata" ); 134 135 return self::METADATA_COMPATIBLE; 136 } 137 138 return self::METADATA_GOOD; 139 } 140 141 /** 142 * @param File $image 143 * @return string 144 */ 145 public function getLongDesc( $image ) { 146 global $wgLang; 147 $original = parent::getLongDesc( $image ); 148 149 Wikimedia\suppressWarnings(); 150 $metadata = unserialize( $image->getMetadata() ); 151 Wikimedia\restoreWarnings(); 152 153 if ( !$metadata || $metadata['frameCount'] <= 0 ) { 154 return $original; 155 } 156 157 $info = []; 158 $info[] = $original; 159 160 if ( $metadata['loopCount'] == 0 ) { 161 $info[] = wfMessage( 'file-info-png-looped' )->parse(); 162 } elseif ( $metadata['loopCount'] > 1 ) { 163 $info[] = wfMessage( 'file-info-png-repeat' )->numParams( $metadata['loopCount'] )->parse(); 164 } 165 166 if ( $metadata['frameCount'] > 0 ) { 167 $info[] = wfMessage( 'file-info-png-frames' )->numParams( $metadata['frameCount'] )->parse(); 168 } 169 170 if ( $metadata['duration'] ) { 171 $info[] = $wgLang->formatTimePeriod( $metadata['duration'] ); 172 } 173 174 return $wgLang->commaList( $info ); 175 } 176 177 /** 178 * Return the duration of an APNG file. 179 * 180 * Shown in the &query=imageinfo&iiprop=size api query. 181 * 182 * @param File $file 183 * @return float The duration of the file. 184 */ 185 public function getLength( $file ) { 186 $serMeta = $file->getMetadata(); 187 Wikimedia\suppressWarnings(); 188 $metadata = unserialize( $serMeta ); 189 Wikimedia\restoreWarnings(); 190 191 if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) { 192 return 0.0; 193 } else { 194 return (float)$metadata['duration']; 195 } 196 } 197 198 // PNGs should be easy to support, but it will need some sharpening applied 199 // and another user test to check if the perceived quality change is noticeable 200 public function supportsBucketing() { 201 return false; 202 } 203} 204