""" Canon CR2 raw image data, version 2.0 image parser. Authors: Fernando Crespo Creation date: 21 february 2017 """ from hachoir.parser import Parser from hachoir.field import SeekableFieldSet, RootSeekableFieldSet, Bytes, String, UInt8, UInt16, UInt32 from hachoir.core.endian import LITTLE_ENDIAN, BIG_ENDIAN from hachoir.core.text_handler import textHandler, hexadecimal from hachoir.parser.image.exif import IFD, IFD_TAGS def getStrips(ifd): data = {} for i, entry in enumerate(ifd.array('entry')): data[entry['tag'].display] = entry # image data if "StripOffsets" in data and "StripByteCounts" in data: offs = ifd.getEntryValues(data["StripOffsets"]) bytes = ifd.getEntryValues(data["StripByteCounts"]) for off, byte in zip(offs, bytes): yield off.value, byte.value class ImageFile(SeekableFieldSet): def __init__(self, parent, name, description, ifd): SeekableFieldSet.__init__(self, parent, name, description, None) self._ifd = ifd def createFields(self): for off, byte in getStrips(self._ifd): self.seekByte(off, relative=False) yield Bytes(self, "strip[]", byte) class CR2File(RootSeekableFieldSet, Parser): PARSER_TAGS = { "id": "cr2", "category": "image", "file_ext": ("cr2",), "mime": ("image/x-canon-cr2",), "min_size": 15, "magic": ((b"CR", 8),), "description": "Canon CR2 raw image data, version 2.0" } # Correct endian is set in constructor endian = LITTLE_ENDIAN def __init__(self, stream, **args): RootSeekableFieldSet.__init__( self, None, "root", stream, None, stream.askSize(self)) if self.stream.readBytes(0, 2) == b"MM": self.endian = BIG_ENDIAN Parser.__init__(self, stream, **args) def validate(self): endian = self.stream.readBytes(0, 2) if endian not in (b"MM", b"II"): return "Invalid endian (%r)" % endian if self["version"].value != 42: return "Unknown Canon TIFF version - " + str(self["version"].value) if self["cr_identifier"].value != "CR": return "Unknown Canon Raw File" return True def createFields(self): iff_start = self.absolute_address yield String(self, "endian", 2, "Endian ('II' or 'MM')", charset="ASCII") if self["endian"].value == "II": self.endian = LITTLE_ENDIAN else: self.endian = BIG_ENDIAN yield UInt16(self, "version", "TIFF version number") yield UInt32(self, "img_dir_ofs", "Next image directory offset") yield String(self, "cr_identifier", 2, "Canon Raw marker", charset="ASCII") yield UInt8(self, "cr_major_version", "Canon Raw major version number") yield UInt8(self, "cr_minor_version", "Canon Raw minor version number") yield textHandler(UInt32(self, "cr_raw_ifd_offset", "Offset to Raw IFD"), hexadecimal) offsets = [(self['img_dir_ofs'].value, 'ifd[]', IFD)] while offsets: offset, name, klass = offsets.pop(0) self.seekByte(offset + iff_start // 8, relative=False) ifd = klass(self, name, iff_start) yield ifd for entry in ifd.array('entry'): tag = entry['tag'].value if tag in IFD_TAGS: name, klass = IFD_TAGS[tag] offsets.append((ifd.getEntryValues(entry)[ 0].value, name + '[]', klass)) if ifd['next'].value != 0: offsets.append((ifd['next'].value, 'ifd[]', IFD)) for ifd in self.array('ifd'): offs = (off for off, byte in getStrips(ifd)) self.seekByte(min(offs), relative=False) image = ImageFile(self, "image[]", "Image File", ifd) yield image