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