45 throw new InvalidArgumentException(
'No file name specified' );
47 if ( !file_exists( $filename ) || is_dir( $filename ) ) {
48 throw new InvalidArgumentException(
"File $filename does not exist" );
51 $fh = fopen( $filename,
'rb' );
54 throw new InvalidArgumentException(
"Unable to open file $filename" );
58 $buf = fread( $fh, 6 );
59 if ( !( $buf ===
'GIF87a' || $buf ===
'GIF89a' ) ) {
60 throw new InvalidArgumentException(
"Not a valid GIF file; header: $buf" );
64 $buf = fread( $fh, 2 );
65 if ( strlen( $buf ) < 2 ) {
66 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read width." );
68 $width = unpack(
'v', $buf )[1];
69 $buf = fread( $fh, 2 );
70 if ( strlen( $buf ) < 2 ) {
71 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read height." );
73 $height = unpack(
'v', $buf )[1];
76 $buf = fread( $fh, 1 );
77 [ $bpp, $have_map ] = self::decodeBPP( $buf );
85 self::readGCT( $fh, $bpp );
88 while ( !feof( $fh ) ) {
89 $buf = fread( $fh, 1 );
92 if ( $buf ===
"\x2C" ) {
101 $buf = fread( $fh, 1 );
102 [ $bpp, $have_map ] = self::decodeBPP( $buf );
106 self::readGCT( $fh, $bpp );
110 self::skipBlock( $fh );
111 } elseif ( $buf ===
"\x21" ) {
113 $buf = fread( $fh, 1 );
114 if ( strlen( $buf ) < 1 ) {
115 throw new InvalidArgumentException(
116 "Not a valid GIF file; Unable to read graphics control extension."
119 $extension_code = unpack(
'C', $buf )[1];
121 if ( $extension_code === 0xF9 ) {
130 $buf = fread( $fh, 2 );
131 if ( strlen( $buf ) < 2 ) {
132 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read delay" );
134 $delay = unpack(
'v', $buf )[1];
135 $duration += $delay * 0.01;
140 $term = fread( $fh, 1 );
141 if ( strlen( $term ) < 1 ) {
142 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read terminator byte" );
144 $term = unpack(
'C', $term )[1];
146 throw new InvalidArgumentException(
"Malformed Graphics Control Extension block" );
148 } elseif ( $extension_code === 0xFE ) {
150 $data = self::readBlock( $fh );
151 if ( $data ===
"" ) {
152 throw new InvalidArgumentException(
'Read error, zero-length comment block' );
160 \UtfNormal\Validator::quickIsNFCVerify( $dataCopy );
162 if ( $dataCopy !== $data ) {
164 $data = @iconv(
'windows-1252',
'UTF-8', $data );
167 $commentCount = count( $comment );
168 if ( $commentCount === 0
170 || $comment[$commentCount - 1] !== $data
177 } elseif ( $extension_code === 0xFF ) {
180 $blockLength = fread( $fh, 1 );
181 if ( strlen( $blockLength ) < 1 ) {
182 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read block length" );
184 $blockLength = unpack(
'C', $blockLength )[1];
185 $data = fread( $fh, $blockLength );
187 if ( $blockLength !== 11 ) {
188 wfDebug( __METHOD__ .
" GIF application block with wrong length" );
189 fseek( $fh, -( $blockLength + 1 ), SEEK_CUR );
190 self::skipBlock( $fh );
195 if ( $data ===
'NETSCAPE2.0' ) {
196 $data = fread( $fh, 2 );
198 if ( $data !==
"\x03\x01" ) {
199 throw new InvalidArgumentException(
"Expected \x03\x01, got $data" );
203 $loopData = fread( $fh, 2 );
204 if ( strlen( $loopData ) < 2 ) {
205 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read loop count" );
207 $loopCount = unpack(
'v', $loopData )[1];
209 if ( $loopCount !== 1 ) {
216 } elseif ( $data ===
'XMP DataXMP' ) {
220 $xmp = self::readBlock( $fh,
true );
222 if ( substr( $xmp, -257, 3 ) !==
"\x01\xFF\xFE"
223 || substr( $xmp, -4 ) !==
"\x03\x02\x01\x00"
225 throw new InvalidArgumentException(
"XMP does not have magic trailer!" );
229 $xmp = substr( $xmp, 0, -257 );
232 fseek( $fh, -( $blockLength + 1 ), SEEK_CUR );
233 self::skipBlock( $fh );
236 self::skipBlock( $fh );
238 } elseif ( $buf ===
"\x3B" ) {
242 if ( strlen( $buf ) < 1 ) {
243 throw new InvalidArgumentException(
"Not a valid GIF file; Unable to read unknown byte." );
245 $byte = unpack(
'C', $buf )[1];
246 throw new InvalidArgumentException(
"At position: " . ftell( $fh ) .
", Unknown byte " . $byte );
251 'frameCount' => $frameCount,
252 'looped' => $isLooped,
253 'duration' => $duration,
255 'comment' => $comment,