1<?php 2/** 3 * Handler for GIF 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 GIF images. 26 * 27 * @ingroup Media 28 */ 29class GIFHandler extends BitmapHandler { 30 /** 31 * Value to store in img_metadata if there was error extracting metadata 32 */ 33 private const BROKEN_FILE = '0'; 34 35 public function getMetadata( $image, $filename ) { 36 try { 37 $parsedGIFMetadata = BitmapMetadataHandler::GIF( $filename ); 38 } catch ( Exception $e ) { 39 // Broken file? 40 wfDebug( __METHOD__ . ': ' . $e->getMessage() ); 41 42 return self::BROKEN_FILE; 43 } 44 45 return serialize( $parsedGIFMetadata ); 46 } 47 48 /** 49 * @param File $image 50 * @param bool|IContextSource $context Context to use (optional) 51 * @return array|bool 52 */ 53 public function formatMetadata( $image, $context = false ) { 54 $meta = $this->getCommonMetaArray( $image ); 55 if ( count( $meta ) === 0 ) { 56 return false; 57 } 58 59 return $this->formatMetadataHelper( $meta, $context ); 60 } 61 62 /** 63 * Return the standard metadata elements for #filemetadata parser func. 64 * @param File $image 65 * @return array|bool 66 */ 67 public function getCommonMetaArray( File $image ) { 68 $meta = $image->getMetadata(); 69 70 if ( !$meta ) { 71 return []; 72 } 73 $meta = unserialize( $meta ); 74 if ( !isset( $meta['metadata'] ) ) { 75 return []; 76 } 77 unset( $meta['metadata']['_MW_GIF_VERSION'] ); 78 79 return $meta['metadata']; 80 } 81 82 /** 83 * @todo Add unit tests 84 * 85 * @param File $image 86 * @return bool 87 */ 88 public function getImageArea( $image ) { 89 $ser = $image->getMetadata(); 90 if ( $ser ) { 91 $metadata = unserialize( $ser ); 92 if ( isset( $metadata['frameCount'] ) && $metadata['frameCount'] > 0 ) { 93 return $image->getWidth() * $image->getHeight() * $metadata['frameCount']; 94 } else { 95 return $image->getWidth() * $image->getHeight(); 96 } 97 } else { 98 return $image->getWidth() * $image->getHeight(); 99 } 100 } 101 102 /** 103 * @param File $image 104 * @return bool 105 */ 106 public function isAnimatedImage( $image ) { 107 $ser = $image->getMetadata(); 108 if ( $ser ) { 109 $metadata = unserialize( $ser ); 110 if ( isset( $metadata['frameCount'] ) && $metadata['frameCount'] > 1 ) { 111 return true; 112 } 113 } 114 115 return false; 116 } 117 118 /** 119 * We cannot animate thumbnails that are bigger than a particular size 120 * @param File $file 121 * @return bool 122 */ 123 public function canAnimateThumbnail( $file ) { 124 global $wgMaxAnimatedGifArea; 125 126 return $this->getImageArea( $file ) <= $wgMaxAnimatedGifArea; 127 } 128 129 public function getMetadataType( $image ) { 130 return 'parsed-gif'; 131 } 132 133 public function isMetadataValid( $image, $metadata ) { 134 if ( $metadata === self::BROKEN_FILE ) { 135 // Do not repetitivly regenerate metadata on broken file. 136 return self::METADATA_GOOD; 137 } 138 139 Wikimedia\suppressWarnings(); 140 $data = unserialize( $metadata ); 141 Wikimedia\restoreWarnings(); 142 143 if ( !$data || !is_array( $data ) ) { 144 wfDebug( __METHOD__ . " invalid GIF metadata" ); 145 146 return self::METADATA_BAD; 147 } 148 149 if ( !isset( $data['metadata']['_MW_GIF_VERSION'] ) 150 || $data['metadata']['_MW_GIF_VERSION'] != GIFMetadataExtractor::VERSION 151 ) { 152 wfDebug( __METHOD__ . " old but compatible GIF metadata" ); 153 154 return self::METADATA_COMPATIBLE; 155 } 156 157 return self::METADATA_GOOD; 158 } 159 160 /** 161 * @param File $image 162 * @return string 163 */ 164 public function getLongDesc( $image ) { 165 global $wgLang; 166 167 $original = parent::getLongDesc( $image ); 168 169 Wikimedia\suppressWarnings(); 170 $metadata = unserialize( $image->getMetadata() ); 171 Wikimedia\restoreWarnings(); 172 173 if ( !$metadata || $metadata['frameCount'] <= 1 ) { 174 return $original; 175 } 176 177 /* Preserve original image info string, but strip the last char ')' so we can add even more */ 178 $info = []; 179 $info[] = $original; 180 181 if ( $metadata['looped'] ) { 182 $info[] = wfMessage( 'file-info-gif-looped' )->parse(); 183 } 184 185 if ( $metadata['frameCount'] > 1 ) { 186 $info[] = wfMessage( 'file-info-gif-frames' )->numParams( $metadata['frameCount'] )->parse(); 187 } 188 189 if ( $metadata['duration'] ) { 190 $info[] = $wgLang->formatTimePeriod( $metadata['duration'] ); 191 } 192 193 return $wgLang->commaList( $info ); 194 } 195 196 /** 197 * Return the duration of the GIF file. 198 * 199 * Shown in the &query=imageinfo&iiprop=size api query. 200 * 201 * @param File $file 202 * @return float The duration of the file. 203 */ 204 public function getLength( $file ) { 205 $serMeta = $file->getMetadata(); 206 Wikimedia\suppressWarnings(); 207 $metadata = unserialize( $serMeta ); 208 Wikimedia\restoreWarnings(); 209 210 if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) { 211 return 0.0; 212 } else { 213 return (float)$metadata['duration']; 214 } 215 } 216} 217