Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
55.93% covered (warning)
55.93%
33 / 59
20.00% covered (danger)
20.00%
2 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
PNGHandler
55.93% covered (warning)
55.93%
33 / 59
20.00% covered (danger)
20.00%
2 / 10
107.02
0.00% covered (danger)
0.00%
0 / 1
 getSizeAndMetadata
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
3.00
 formatMetadata
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getCommonMetaArray
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
 isAnimatedImage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 canAnimateThumbnail
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMetadataType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isFileMetadataValid
81.82% covered (warning)
81.82%
9 / 11
0.00% covered (danger)
0.00%
0 / 1
6.22
 getLongDesc
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
72
 getLength
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 supportsBucketing
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
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
24use MediaWiki\Context\IContextSource;
25use MediaWiki\FileRepo\File\File;
26use Wikimedia\RequestTimeout\TimeoutException;
27
28/**
29 * Handler for PNG images.
30 *
31 * @ingroup Media
32 */
33class PNGHandler extends BitmapHandler {
34    private const BROKEN_FILE = '0';
35
36    /**
37     * @param MediaHandlerState $state
38     * @param string $filename
39     * @return array
40     */
41    public function getSizeAndMetadata( $state, $filename ) {
42        try {
43            $metadata = BitmapMetadataHandler::PNG( $filename );
44        } catch ( TimeoutException $e ) {
45            throw $e;
46        } catch ( Exception $e ) {
47            // Broken file?
48            wfDebug( __METHOD__ . ': ' . $e->getMessage() );
49
50            return [ 'metadata' => [ '_error' => self::BROKEN_FILE ] ];
51        }
52
53        return [
54            'width' => $metadata['width'],
55            'height' => $metadata['height'],
56            'bits' => $metadata['bitDepth'],
57            'metadata' => array_diff_key(
58                $metadata,
59                [ 'width' => true, 'height' => true, 'bits' => true ]
60            )
61        ];
62    }
63
64    /**
65     * @param File $image
66     * @param IContextSource|false $context
67     * @return array[]|false
68     */
69    public function formatMetadata( $image, $context = false ) {
70        $meta = $this->getCommonMetaArray( $image );
71        if ( !$meta ) {
72            return false;
73        }
74
75        return $this->formatMetadataHelper( $meta, $context );
76    }
77
78    /**
79     * Get a file type independent array of metadata.
80     *
81     * @param File $image
82     * @return array The metadata array
83     */
84    public function getCommonMetaArray( File $image ) {
85        $meta = $image->getMetadataArray();
86
87        if ( !isset( $meta['metadata'] ) ) {
88            return [];
89        }
90        unset( $meta['metadata']['_MW_PNG_VERSION'] );
91
92        return $meta['metadata'];
93    }
94
95    /**
96     * @param File $image
97     * @return bool
98     */
99    public function isAnimatedImage( $image ) {
100        $metadata = $image->getMetadataArray();
101        return isset( $metadata['frameCount'] ) && $metadata['frameCount'] > 1;
102    }
103
104    /**
105     * We do not support making APNG thumbnails, so always false
106     * @param File $image
107     * @return bool False
108     */
109    public function canAnimateThumbnail( $image ) {
110        return false;
111    }
112
113    public function getMetadataType( $image ) {
114        return 'parsed-png';
115    }
116
117    public function isFileMetadataValid( $image ) {
118        $data = $image->getMetadataArray();
119        if ( $data === [ '_error' => self::BROKEN_FILE ] ) {
120            // Do not repetitively regenerate metadata on broken file.
121            return self::METADATA_GOOD;
122        }
123
124        if ( !$data || isset( $data['_error'] ) ) {
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        $metadata = $image->getMetadataArray();
150
151        if ( !$metadata || isset( $metadata['_error'] ) || $metadata['frameCount'] <= 0 ) {
152            return $original;
153        }
154
155        $info = [];
156        $info[] = $original;
157
158        if ( $metadata['loopCount'] == 0 ) {
159            $info[] = wfMessage( 'file-info-png-looped' )->parse();
160        } elseif ( $metadata['loopCount'] > 1 ) {
161            $info[] = wfMessage( 'file-info-png-repeat' )->numParams( $metadata['loopCount'] )->parse();
162        }
163
164        if ( $metadata['frameCount'] > 0 ) {
165            $info[] = wfMessage( 'file-info-png-frames' )->numParams( $metadata['frameCount'] )->parse();
166        }
167
168        if ( $metadata['duration'] ) {
169            $info[] = $wgLang->formatTimePeriod( $metadata['duration'] );
170        }
171
172        return $wgLang->commaList( $info );
173    }
174
175    /**
176     * Return the duration of an APNG file.
177     *
178     * Shown in the &query=imageinfo&iiprop=size api query.
179     *
180     * @param File $file
181     * @return float The duration of the file.
182     */
183    public function getLength( $file ) {
184        $metadata = $file->getMetadataArray();
185
186        if ( !$metadata || !isset( $metadata['duration'] ) || !$metadata['duration'] ) {
187            return 0.0;
188        }
189
190        return (float)$metadata['duration'];
191    }
192
193    // PNGs should be easy to support, but it will need some sharpening applied
194    // and another user test to check if the perceived quality change is noticeable
195    public function supportsBucketing() {
196        return false;
197    }
198}