5 * @covers \PNGMetadataExtractor
7 class PNGMetadataExtractorTest
extends MediaWikiIntegrationTestCase
{
8 private const FILE_PATH
= __DIR__
. '/../../data/media/';
11 * Tests zTXt tag (compressed textual metadata)
12 * @requires extension zlib
14 public function testPngNativetZtxt() {
15 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
16 'Png-native-test.png' );
17 $expected = "foo bar baz foo foo foo foof foo foo foo foo";
18 $this->assertArrayHasKey( 'text', $meta );
19 $meta = $meta['text'];
20 $this->assertArrayHasKey( 'Make', $meta );
21 $this->assertArrayHasKey( 'x-default', $meta['Make'] );
23 $this->assertEquals( $expected, $meta['Make']['x-default'] );
27 * Test tEXt tag (Uncompressed textual metadata)
29 public function testPngNativeText() {
30 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
31 'Png-native-test.png' );
32 $expected = "Some long image desc";
33 $this->assertArrayHasKey( 'text', $meta );
34 $meta = $meta['text'];
35 $this->assertArrayHasKey( 'ImageDescription', $meta );
36 $this->assertArrayHasKey( 'x-default', $meta['ImageDescription'] );
37 $this->assertArrayHasKey( '_type', $meta['ImageDescription'] );
39 $this->assertEquals( $expected, $meta['ImageDescription']['x-default'] );
43 * tEXt tags must be encoded iso-8859-1 (vs iTXt which are utf-8)
44 * Make sure non-ascii characters get converted properly
46 public function testPngNativeTextNonAscii() {
47 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
48 'Png-native-test.png' );
50 // Note the Copyright symbol here is a utf-8 one
51 // (aka \xC2\xA9) where in the file its iso-8859-1
52 // encoded as just \xA9.
53 $expected = "© 2010 Bawolff";
55 $this->assertArrayHasKey( 'text', $meta );
56 $meta = $meta['text'];
57 $this->assertArrayHasKey( 'Copyright', $meta );
58 $this->assertArrayHasKey( 'x-default', $meta['Copyright'] );
60 $this->assertEquals( $expected, $meta['Copyright']['x-default'] );
64 * Given a normal static PNG, check the animation metadata returned.
66 public function testStaticPngAnimationMetadata() {
67 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
68 'Png-native-test.png' );
70 $this->assertSame( 0, $meta['frameCount'] );
71 $this->assertSame( 1, $meta['loopCount'] );
72 $this->assertSame( 0.0, $meta['duration'] );
76 * Given an animated APNG image file
77 * check it gets animated metadata right.
79 public function testApngAnimationMetadata() {
80 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
81 'Animated_PNG_example_bouncing_beach_ball.png' );
83 $this->assertEquals( 20, $meta['frameCount'] );
84 // Note loop count of 0 = infinity
85 $this->assertSame( 0, $meta['loopCount'] );
86 $this->assertEqualsWithDelta( 1.5, $meta['duration'], 0.00001, '' );
89 public function testPngBitDepth8() {
90 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
91 'Png-native-test.png' );
93 $this->assertEquals( 8, $meta['bitDepth'] );
96 public function testPngBitDepth1() {
97 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
99 $this->assertSame( 1, $meta['bitDepth'] );
102 public function testPngIndexColour() {
103 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
104 'Png-native-test.png' );
106 $this->assertEquals( 'index-coloured', $meta['colorType'] );
109 public function testPngRgbColour() {
110 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
112 $this->assertEquals( 'truecolour-alpha', $meta['colorType'] );
115 public function testPngRgbNoAlphaColour() {
116 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
118 $this->assertEquals( 'truecolour', $meta['colorType'] );
121 public function testPngGreyscaleColour() {
122 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
123 'greyscale-png.png' );
124 $this->assertEquals( 'greyscale-alpha', $meta['colorType'] );
127 public function testPngGreyscaleNoAlphaColour() {
128 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
129 'greyscale-na-png.png' );
130 $this->assertEquals( 'greyscale', $meta['colorType'] );
134 * T286273 -- tEXt chunk replaced by null bytes
136 public function testPngInvalidChunk() {
137 $meta = PNGMetadataExtractor
::getMetadata( self
::FILE_PATH
.
138 'tEXt-invalid-masked.png' );
139 $this->assertEquals( 10, $meta['width'] );
140 $this->assertEquals( 10, $meta['height'] );
144 * T286273 -- oversize chunk
146 public function testPngOversizeChunk() {
147 // Write a temporary file consisting of a normal PNG plus an extra tEXt chunk.
148 // Try to hold the chunk in memory only once.
149 $path = $this->getNewTempFile();
150 copy( self
::FILE_PATH
. '1bit-png.png', $path );
151 $chunkTypeAndData = "tEXtkey\0value" . str_repeat( '.', 10000000 );
152 $crc = crc32( $chunkTypeAndData );
153 $chunkLength = strlen( $chunkTypeAndData ) - 4;
154 $file = fopen( $path, 'r+' );
155 fseek( $file, -12, SEEK_END
);
156 $iend = fread( $file, 12 );
157 fseek( $file, -12, SEEK_END
);
158 fwrite( $file, pack( 'N', $chunkLength ) );
159 fwrite( $file, $chunkTypeAndData );
160 fwrite( $file, pack( 'N', $crc ) );
161 fwrite( $file, $iend );
164 // Extract the metadata
165 $meta = PNGMetadataExtractor
::getMetadata( $path );
166 $this->assertEquals( 50, $meta['width'] );
167 $this->assertEquals( 50, $meta['height'] );
169 // Verify that the big chunk didn't end up in the metadata
170 $this->assertLessThan( 100000, strlen( serialize( $meta ) ) );