Skip to content

Commit

Permalink
Fix GIF parser
Browse files Browse the repository at this point in the history
Previously, the GIF parser would assume a GIF is animated if it had the
Netscape Application Extension. This can lead to false positives where a
GIF can have all the indicators of an animated GIF but only one Image
Descriptor.

Now, the code checks for the absence of the Graphic Control Extension
before an Image Descriptor to mean non-animated and otherwise checks if
there are multiple Image Descriptors. This unfortunately requires
parsing much further into the data stream.

The example cli now also supports specifiying the buffer size.
  • Loading branch information
ojii committed Aug 22, 2024
1 parent a701f07 commit e995c3d
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 42 deletions.
187 changes: 187 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ crate-type = ["cdylib", "lib"]
[dependencies]
byteorder = "1.5.0"
pyo3 = "0.22.1"
clap = { version = "4.5.16", features = ["derive"] }

[dev-dependencies]
clap = { version = "4.5.16", features = ["derive"] }
paste = "1.0.15"
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"

[[example]]
name = "cli"
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ else:
size.is_animated
```

You should not pass the entire image data, the first kilobyte or so should suffice.
You should not pass the entire image data, the first kilobyte or so should suffice for most formats, other than GIF
where a larger amount of data may be required to determine whether the image is animated or not.

### API

Expand Down Expand Up @@ -60,6 +61,11 @@ that does actual image parsing to determine if the data is actually an image.
and does not necessarily support all features or variants of those formats, as a result, there might be
false positives and false negatives.

## Example CLI

You can use `cargo example --cli` for a simple command line tool to try out this library. See
`cargo example --cli -- --help` for details.

## Building

Use [maturin](https://www.maturin.rs/) to build: `maturin build`
Expand Down
26 changes: 19 additions & 7 deletions examples/cli.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
extern crate imgsize;
use clap::Parser;
use std::fs::File;
use std::io;
use std::io::Read;
use std::{env, io};
use std::path::PathBuf;

#[derive(Parser)]
struct Arguments {
#[arg(short, long, default_value_t = 1024)]
buf_size: usize,
paths: Vec<PathBuf>,
}

pub fn main() -> io::Result<()> {
let mut buffer = [0u8; 1024];
for path in env::args().skip(1) {
let arguments = Arguments::parse();
let mut buffer = vec![0u8; arguments.buf_size];
for path in arguments.paths.iter() {
let name = path.to_str().unwrap();
let mut file = File::open(&path)?;
file.read(&mut buffer)?;
match imgsize::get_size(&buffer) {
let read = file.read(&mut buffer)?;
match imgsize::get_size(&buffer[..read]) {
Some(size) => println!(
"{}: {}x{}, {}, animated={}",
path, size.width, size.height, size.mime_type, size.is_animated
name, size.width, size.height, size.mime_type, size.is_animated
),
None => println!("{}: unsupported format", path),
None => println!("{}: unsupported format", name),
}
buffer.fill(0);
}
Ok(())
}
6 changes: 2 additions & 4 deletions python-tests/test_sample_files.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import json

import pytest
from conftest import ROOT

BYTES_TO_READ = 1024
from conftest import ROOT


def find_examples():
Expand All @@ -12,7 +10,7 @@ def find_examples():
output_path = input_path.with_suffix('.output')
if not output_path.exists(): continue
with input_path.open('rb') as fobj:
data = fobj.read(BYTES_TO_READ)
data = fobj.read()
with output_path.open('r') as fobj:
output = json.load(fobj)
yield pytest.param(data, output, id=input_path.stem)
Expand Down
Loading

0 comments on commit e995c3d

Please sign in to comment.