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