Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
36.54% |
19 / 52 |
|
14.29% |
1 / 7 |
CRAP | |
0.00% |
0 / 1 |
| XCFHandler | |
36.54% |
19 / 52 |
|
14.29% |
1 / 7 |
100.81 | |
0.00% |
0 / 1 |
| mustRender | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getThumbType | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getXCFMetaData | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
20 | |||
| getSizeAndMetadata | |
76.19% |
16 / 21 |
|
0.00% |
0 / 1 |
6.49 | |||
| isFileMetadataValid | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| hasGDSupport | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| canRender | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Handler for the Gimp's native file format (XCF) |
| 4 | * |
| 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 |
| 9 | * |
| 10 | * @license GPL-2.0-or-later |
| 11 | * @file |
| 12 | * @ingroup Media |
| 13 | */ |
| 14 | |
| 15 | use MediaWiki\FileRepo\File\File; |
| 16 | use Wikimedia\StringUtils\StringUtils; |
| 17 | use Wikimedia\UnpackFailedException; |
| 18 | |
| 19 | /** |
| 20 | * Handler for the Gimp's native file format; getimagesize() doesn't |
| 21 | * support these files |
| 22 | * |
| 23 | * @ingroup Media |
| 24 | */ |
| 25 | class XCFHandler extends BitmapHandler { |
| 26 | /** |
| 27 | * @param File $file |
| 28 | * @return bool |
| 29 | */ |
| 30 | public function mustRender( $file ) { |
| 31 | return true; |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Render files as PNG |
| 36 | * |
| 37 | * @param string $ext |
| 38 | * @param string $mime |
| 39 | * @param array|null $params |
| 40 | * @return array |
| 41 | */ |
| 42 | public function getThumbType( $ext, $mime, $params = null ) { |
| 43 | return [ 'png', 'image/png' ]; |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * Metadata for a given XCF file |
| 48 | * |
| 49 | * Will return false if file magic signature is not recognized |
| 50 | * @author Hexmode |
| 51 | * @author Hashar |
| 52 | * |
| 53 | * @param string $filename Full path to a XCF file |
| 54 | * @return array|null Metadata Array just like PHP getimagesize() |
| 55 | */ |
| 56 | private static function getXCFMetaData( $filename ) { |
| 57 | # Decode master structure |
| 58 | $f = fopen( $filename, 'rb' ); |
| 59 | if ( !$f ) { |
| 60 | return null; |
| 61 | } |
| 62 | # The image structure always starts at offset 0 in the XCF file. |
| 63 | # So we just read it :-) |
| 64 | $binaryHeader = fread( $f, 26 ); |
| 65 | fclose( $f ); |
| 66 | |
| 67 | /** |
| 68 | * Master image structure: |
| 69 | * |
| 70 | * byte[9] "gimp xcf " File type magic |
| 71 | * byte[4] version XCF version |
| 72 | * "file" - version 0 |
| 73 | * "v001" - version 1 |
| 74 | * "v002" - version 2 |
| 75 | * byte 0 Zero-terminator for version tag |
| 76 | * uint32 width With of canvas |
| 77 | * uint32 height Height of canvas |
| 78 | * uint32 base_type Color mode of the image; one of |
| 79 | * 0: RGB color |
| 80 | * 1: Grayscale |
| 81 | * 2: Indexed color |
| 82 | * (enum GimpImageBaseType in libgimpbase/gimpbaseenums.h) |
| 83 | */ |
| 84 | try { |
| 85 | $header = StringUtils::unpack( |
| 86 | "A9magic" . # A: space padded |
| 87 | "/a5version" . # a: zero padded |
| 88 | "/Nwidth" . # \ |
| 89 | "/Nheight" . # N: unsigned long 32bit big endian |
| 90 | "/Nbase_type", # / |
| 91 | $binaryHeader |
| 92 | ); |
| 93 | } catch ( UnpackFailedException ) { |
| 94 | return null; |
| 95 | } |
| 96 | |
| 97 | # Check values |
| 98 | if ( $header['magic'] !== 'gimp xcf' ) { |
| 99 | wfDebug( __METHOD__ . " '$filename' has invalid magic signature." ); |
| 100 | |
| 101 | return null; |
| 102 | } |
| 103 | # TODO: we might want to check for correct values of width and height |
| 104 | |
| 105 | wfDebug( __METHOD__ . |
| 106 | ": canvas size of '$filename' is {$header['width']} x {$header['height']} px" ); |
| 107 | |
| 108 | return $header; |
| 109 | } |
| 110 | |
| 111 | /** @inheritDoc */ |
| 112 | public function getSizeAndMetadata( $state, $filename ) { |
| 113 | $header = self::getXCFMetaData( $filename ); |
| 114 | $metadata = []; |
| 115 | if ( $header ) { |
| 116 | // Try to be consistent with the names used by PNG files. |
| 117 | // Unclear from base media type if it has an alpha layer, |
| 118 | // so just assume that it does since it "potentially" could. |
| 119 | switch ( $header['base_type'] ) { |
| 120 | case 0: |
| 121 | $metadata['colorType'] = 'truecolour-alpha'; |
| 122 | break; |
| 123 | case 1: |
| 124 | $metadata['colorType'] = 'greyscale-alpha'; |
| 125 | break; |
| 126 | case 2: |
| 127 | $metadata['colorType'] = 'index-coloured'; |
| 128 | break; |
| 129 | default: |
| 130 | $metadata['colorType'] = 'unknown'; |
| 131 | } |
| 132 | } else { |
| 133 | // Marker to prevent repeated attempted extraction |
| 134 | $metadata['error'] = true; |
| 135 | } |
| 136 | return [ |
| 137 | 'width' => $header['width'] ?? 0, |
| 138 | 'height' => $header['height'] ?? 0, |
| 139 | 'bits' => 8, |
| 140 | 'metadata' => $metadata |
| 141 | ]; |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Should we refresh the metadata |
| 146 | * |
| 147 | * @param File $file The file object for the file in question |
| 148 | * @return bool|int One of the self::METADATA_(BAD|GOOD|COMPATIBLE) constants |
| 149 | */ |
| 150 | public function isFileMetadataValid( $file ) { |
| 151 | if ( !$file->getMetadataArray() ) { |
| 152 | // Old metadata when we just put an empty string in there |
| 153 | return self::METADATA_BAD; |
| 154 | } |
| 155 | |
| 156 | return self::METADATA_GOOD; |
| 157 | } |
| 158 | |
| 159 | /** @inheritDoc */ |
| 160 | protected function hasGDSupport() { |
| 161 | return false; |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Can we render this file? |
| 166 | * |
| 167 | * Image magick doesn't support indexed xcf files as of current |
| 168 | * writing (as of 6.8.9-3) |
| 169 | * @param File $file |
| 170 | * @return bool |
| 171 | */ |
| 172 | public function canRender( $file ) { |
| 173 | $xcfMeta = $file->getMetadataArray(); |
| 174 | if ( isset( $xcfMeta['colorType'] ) && $xcfMeta['colorType'] === 'index-coloured' ) { |
| 175 | return false; |
| 176 | } |
| 177 | return parent::canRender( $file ); |
| 178 | } |
| 179 | } |