Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added example/asset/non_standard_ending.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ class GifDecoder extends BaseDecoder with MutilFileHeaderAndFooterValidator {
MutilFileHeaderAndFooter get headerAndFooter => _GifInfo();
}

/// Internal class that defines GIF file format validation rules.
///
/// This class supports both standard and non-standard GIF files:
/// - Standard GIF files end with the 0x3B trailer byte
/// - Non-standard GIF files may omit the 0x3B trailer
///
/// The relaxed footer validation (accepting files without 0x3B) is intentional
/// because:
/// 1. Some valid GIF files produced by certain encoders don't include the trailer
/// 2. The GIF size information is stored in the header (bytes 6-10), not the footer
/// 3. The primary purpose of this library is to extract image dimensions, not
/// to perform comprehensive file validation
/// 4. The GIF header is still validated, ensuring basic format correctness
class _GifInfo with MutilFileHeaderAndFooter {
static const start89a = [
0x47,
Expand All @@ -60,9 +73,14 @@ class _GifInfo with MutilFileHeaderAndFooter {
];

static const end = [0x3B];

// Allow GIF files without the standard 0x3B trailer.
// Some GIF encoders create valid GIF files without the trailer byte.
// We provide both options: with trailer and without (empty list).
static const emptyEnd = <int>[];

@override
List<List<int>> get mutipleEndBytesList => [end];
List<List<int>> get mutipleEndBytesList => [end, emptyEnd];

@override
List<List<int>> get mutipleStartBytesList => [
Expand Down
66 changes: 66 additions & 0 deletions packages/image_size_getter/test/non_standard_gif_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'dart:io';

import 'package:image_size_getter/file_input.dart';
import 'package:image_size_getter/image_size_getter.dart';
import 'package:test/test.dart';

void main() {
group('Test non-standard GIF (without 0x3B ending)', () {
test('Test GIF without 0x3B trailer', () {
final gif = File('../../example/asset/non_standard_ending.gif');

const GifDecoder decoder = GifDecoder();
final input = FileInput(gif);

// The file should still be valid even without 0x3B ending
expect(decoder.isValid(input), isTrue,
reason: 'GIF should be valid even without 0x3B trailer');

// Size should still be readable
expect(decoder.getSize(input), Size(200, 150));
});

test('Test standard GIF with 0x3B trailer for comparison', () {
final gif = File('../../example/asset/87a.gif');

const GifDecoder decoder = GifDecoder();
final input = FileInput(gif);

expect(decoder.isValid(input), isTrue);
expect(decoder.getSize(input), Size(200, 150));
});

test('Test GIF89a format with and without trailer', () {
final gif89a = File('../../example/asset/dialog.gif');

const GifDecoder decoder = GifDecoder();
final input = FileInput(gif89a);

// Standard GIF89a should still work
expect(decoder.isValid(input), isTrue);
expect(decoder.getSize(input), Size(688, 1326));
});
Comment on lines +33 to +42
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name and comment suggest testing "with and without trailer" but this test only validates a single standard GIF file (dialog.gif) that presumably has the 0x3B trailer. To match the test description, consider either renaming this test to clarify it's only testing the standard format, or adding a corresponding test case for a GIF89a file without a trailer (similar to non_standard_ending.gif).

Copilot uses AI. Check for mistakes.

test('Test that invalid headers are still rejected', () {
// Create a file with invalid header in a temporary directory
final tempDir = Directory.systemTemp.createTempSync('gif_test_');
final tempFile = File('${tempDir.path}/temp_invalid.gif');

try {
tempFile.writeAsBytesSync([0x47, 0x49, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);

const GifDecoder decoder = GifDecoder();
final input = FileInput(tempFile);

// Invalid GIF header should be rejected
expect(decoder.isValid(input), isFalse,
reason: 'File with invalid GIF header should be rejected');
} finally {
// Clean up temp directory and all its contents
if (tempDir.existsSync()) {
tempDir.deleteSync(recursive: true);
}
}
});
});
}
Loading