diff --git a/README.rst b/README.rst index b4039c3..19098d5 100644 --- a/README.rst +++ b/README.rst @@ -67,6 +67,7 @@ Image - **jpg** - ``image/jpeg`` - **jpx** - ``image/jpx`` - **png** - ``image/png`` +- **apng** - ``image/apng`` - **gif** - ``image/gif`` - **webp** - ``image/webp`` - **cr2** - ``image/x-canon-cr2`` diff --git a/filetype/types/__init__.py b/filetype/types/__init__.py index e513edb..70d8789 100644 --- a/filetype/types/__init__.py +++ b/filetype/types/__init__.py @@ -16,6 +16,7 @@ image.Xcf(), image.Jpeg(), image.Jpx(), + image.Apng(), image.Png(), image.Gif(), image.Webp(), diff --git a/filetype/types/image.py b/filetype/types/image.py index ed1779c..b64c674 100644 --- a/filetype/types/image.py +++ b/filetype/types/image.py @@ -48,6 +48,46 @@ def match(self, buf): ) +class Apng(Type): + """ + Implements the APNG image type matcher. + """ + MIME = 'image/apng' + EXTENSION = 'apng' + + def __init__(self): + super(Apng, self).__init__( + mime=Apng.MIME, + extension=Apng.EXTENSION + ) + + def match(self, buf): + if(len(buf) > 8 and + buf[:8] == bytearray([0x89, 0x50, 0x4e, 0x47, + 0x0d, 0x0a, 0x1a, 0x0a])): + #cursor in buf, skip already readed 8 bytes + i = 8 + while len(buf) > i: + data_length = int.from_bytes(buf[i:i+4], byteorder="big") + i += 4 + + chunk_type = buf[i:i+4].decode("ascii") + i += 4 + + #acTL chunk in APNG should appears first than IDAT + #IEND is end of PNG + if (chunk_type == "IDAT" or chunk_type == "IEND"): + return 0 + elif (chunk_type == "acTL"): + return 1 + + #move to the next chunk by skipping data and crc (4 bytes) + i += data_length + 4 + return 0 + else: + return 0 + + class Png(Type): """ Implements the PNG image type matcher.