Merge "Special:Upload should not crash on failing previews"
[mediawiki.git] / includes / media / XCF.php
blobc41952408f22101a00bcfe323cd78d7fb9ea2352
1 <?php
2 /**
3 * Handler for the Gimp's native file format (XCF)
5 * Overview:
6 * https://en.wikipedia.org/wiki/XCF_(file_format)
7 * Specification in Gnome repository:
8 * http://svn.gnome.org/viewvc/gimp/trunk/devel-docs/xcf.txt?view=markup
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/gpl.html
25 * @file
26 * @ingroup Media
29 /**
30 * Handler for the Gimp's native file format; getimagesize() doesn't
31 * support these files
33 * @ingroup Media
35 class XCFHandler extends BitmapHandler {
36 /**
37 * @param File $file
38 * @return bool
40 public function mustRender( $file ) {
41 return true;
44 /**
45 * Render files as PNG
47 * @param string $ext
48 * @param string $mime
49 * @param array $params
50 * @return array
52 function getThumbType( $ext, $mime, $params = null ) {
53 return [ 'png', 'image/png' ];
56 /**
57 * Get width and height from the XCF header.
59 * @param File|FSFile $image
60 * @param string $filename
61 * @return array
63 function getImageSize( $image, $filename ) {
64 $header = self::getXCFMetaData( $filename );
65 if ( !$header ) {
66 return false;
69 # Forge a return array containing metadata information just like getimagesize()
70 # See PHP documentation at: https://secure.php.net/getimagesize
71 return [
72 0 => $header['width'],
73 1 => $header['height'],
74 2 => null, # IMAGETYPE constant, none exist for XCF.
75 3 => "height=\"{$header['height']}\" width=\"{$header['width']}\"",
76 'mime' => 'image/x-xcf',
77 'channels' => null,
78 'bits' => 8, # Always 8-bits per color
82 /**
83 * Metadata for a given XCF file
85 * Will return false if file magic signature is not recognized
86 * @author Hexmode
87 * @author Hashar
89 * @param string $filename Full path to a XCF file
90 * @return bool|array Metadata Array just like PHP getimagesize()
92 static function getXCFMetaData( $filename ) {
93 # Decode master structure
94 $f = fopen( $filename, 'rb' );
95 if ( !$f ) {
96 return false;
98 # The image structure always starts at offset 0 in the XCF file.
99 # So we just read it :-)
100 $binaryHeader = fread( $f, 26 );
101 fclose( $f );
104 * Master image structure:
106 * byte[9] "gimp xcf " File type magic
107 * byte[4] version XCF version
108 * "file" - version 0
109 * "v001" - version 1
110 * "v002" - version 2
111 * byte 0 Zero-terminator for version tag
112 * uint32 width With of canvas
113 * uint32 height Height of canvas
114 * uint32 base_type Color mode of the image; one of
115 * 0: RGB color
116 * 1: Grayscale
117 * 2: Indexed color
118 * (enum GimpImageBaseType in libgimpbase/gimpbaseenums.h)
120 try {
121 $header = wfUnpack(
122 "A9magic" . # A: space padded
123 "/a5version" . # a: zero padded
124 "/Nwidth" . # \
125 "/Nheight" . # N: unsigned long 32bit big endian
126 "/Nbase_type", # /
127 $binaryHeader
129 } catch ( Exception $mwe ) {
130 return false;
133 # Check values
134 if ( $header['magic'] !== 'gimp xcf' ) {
135 wfDebug( __METHOD__ . " '$filename' has invalid magic signature.\n" );
137 return false;
139 # TODO: we might want to check for sane values of width and height
141 wfDebug( __METHOD__ .
142 ": canvas size of '$filename' is {$header['width']} x {$header['height']} px\n" );
144 return $header;
148 * Store the channel type
150 * Greyscale files need different command line options.
152 * @param File|FSFile $file The image object, or false if there isn't one.
153 * Warning, FSFile::getPropsFromPath might pass an (object)array() instead (!)
154 * @param string $filename The filename
155 * @return string
157 public function getMetadata( $file, $filename ) {
158 $header = self::getXCFMetaData( $filename );
159 $metadata = [];
160 if ( $header ) {
161 // Try to be consistent with the names used by PNG files.
162 // Unclear from base media type if it has an alpha layer,
163 // so just assume that it does since it "potentially" could.
164 switch ( $header['base_type'] ) {
165 case 0:
166 $metadata['colorType'] = 'truecolour-alpha';
167 break;
168 case 1:
169 $metadata['colorType'] = 'greyscale-alpha';
170 break;
171 case 2:
172 $metadata['colorType'] = 'index-coloured';
173 break;
174 default:
175 $metadata['colorType'] = 'unknown';
178 } else {
179 // Marker to prevent repeated attempted extraction
180 $metadata['error'] = true;
182 return serialize( $metadata );
186 * Should we refresh the metadata
188 * @param File $file The file object for the file in question
189 * @param string $metadata Serialized metadata
190 * @return bool One of the self::METADATA_(BAD|GOOD|COMPATIBLE) constants
192 public function isMetadataValid( $file, $metadata ) {
193 if ( !$metadata ) {
194 // Old metadata when we just put an empty string in there
195 return self::METADATA_BAD;
196 } else {
197 return self::METADATA_GOOD;
202 * Must use "im" for XCF
204 * @param string $dstPath
205 * @param bool $checkDstPath
206 * @return string
208 protected function getScalerType( $dstPath, $checkDstPath = true ) {
209 return "im";
213 * Can we render this file?
215 * Image magick doesn't support indexed xcf files as of current
216 * writing (as of 6.8.9-3)
217 * @param File $file
218 * @return bool
220 public function canRender( $file ) {
221 MediaWiki\suppressWarnings();
222 $xcfMeta = unserialize( $file->getMetadata() );
223 MediaWiki\restoreWarnings();
224 if ( isset( $xcfMeta['colorType'] ) && $xcfMeta['colorType'] === 'index-coloured' ) {
225 return false;
227 return parent::canRender( $file );