def parse(self): """ Parse the structure. """ # The first four bytes of a data structure should always be the ASCII # string 8BIM data = self.read(4) if (data == "8BIM"): # The next two bytes specify the resource ID (tag number) tag_num_data = self.read(2) if (tag_num_data): tag_num = byteform.btousi(tag_num_data, big_endian=self.big_endian) # What then follows is the "Pascal string". The first byte determines its # length. If the total is an uneven number, it is padded with a \x00 # character. We don't need this string, so we step over it. ps_len = byteform.btousi(self.read(1), big_endian=self.big_endian) if ((ps_len % 2) == 0): ps_len += 1 self.read(ps_len) # Now it's getting interesting; the next four bytes determine the length # of the data data_len = byteform.btousi(self.read(4), big_endian=self.big_endian) # Store the byte position and data length in the tags dict self.tags[tag_num] = datablock.DataBlock( self.fp, self.byte_pos, data_len) # Skip to the next structure self.read(data_len)
def parse(self): """ Parse the IPTC block. """ # The IPTC data is structured in a very simple way; as a lineary stream of # tags and the associated data. Tags can belong to different segments, but # this segment number is simply written in front of each tag. #Each tag starts with the bye 0x1C try: start_byte = self.read(1) except IOError: start_byte = None while (start_byte == "\x1c"): # The next byte specifies the record number record_num = byteform.btousi(self.read(1)) # Then follows the tag number tag_type = byteform.btousi(self.read(1)) # The next two bytes determine the payload length, or the length of the # fields specifying the payload length if we have an extended tag. length = byteform.btousi(self.read(2), big_endian=self.big_endian) # If the most significant bit is 1, we have an extended tag if (length & 32768): # 10000000 00000000 # We have an extended tag length_count = length & 32767 # 01111111 11111111 length = byteform.btousi(self.read(length_count), big_endian=self.big_endian) # Construct the tag and append it to the list tag_obj = datablock.DataBlock(self.fp, self.tell() + self.getDataOffset(), length) record = self.records.query("num", record_num, "record") if (tag_type in record.fields): record.fields[tag_type].append(tag_obj) else: record.fields[tag_type] = [tag_obj] # Seek to the next read position and read the new first byte self.seek(self.tell() + length) try: start_byte = self.read(1) except IOError: break self.parsed = True
def __init__(self, file_pointer=None, ifd_offset=0, header_offset=0, data=None, big_endian=True): # Create a temporary Datablock to parse the data block = datablock.DataBlock(fp=file_pointer, offset=ifd_offset, data=data) # Read the header string header = block.read(len(self.header_str)) if (header != self.header_str) and (header != ""): raise "No valid Makernote!" # Read the rest of the header and save it for writing self.extra_bytes = block.read(self.header_length - len(self.header_string)) # Initialize the IFD ifd.IFD.__init__(self, file_pointer, ifd_offset + self.header_length, header_offset, data, big_endian)
def __init__(self, file_pointer=None, ifd_offset=0, header_offset=0, data=None, big_endian=False): # Fujifilm always uses little endian block = datablock.DataBlock(fp=file_pointer, offset=ifd_offset + header_offset, data=data) header = block.read(8) if (header == None): mn_offset = 0 elif (header == "FUJIFILM"): mn_offset = byteform.btousi(block.read(4), big_endian=False) else: raise "No valid Fujifilm Makernote!" ifd.IFD.__init__(self, file_pointer, mn_offset, ifd_offset + header_offset, data, big_endian=False)
def __getTagObj__(self, tag_num, payload=None, check=True, data_type=None, count=None, data=None): """ Helper method to prepare a tag object for setTag and appendTag. """ # Get the index and check if we can set this tag index = self.tags.query("num", tag_num) if (index is False): if (check): raise KeyError, "Tag %d is not known in this record!" % tag_num else: if not ((data_type) or (data)): raise KeyError, "Unknown tag %d, and no further way to encode it" % tag_num # If the user wants to set the data herself, check if its of the correct # type if (data): if (type(data) != types.StringType): raise TypeError, "IPTC data should be a binary string" # Otherwise, encode it elif (payload): # Find out the data type for the tag num if (not data_type): data_type = self.tags.query(index, "data_type") # Retrieve the allowed lengths if (not count): count = self.tags.query(index, "count") if (type(count) == types.ListType): min_count, max_count = count else: min_count = count max_count = count # Check if the payload has enough data if (type(payload) not in [types.ListType, types.TupleType, types.StringType]): payload = [payload] if ( (min_count) and (max_count) ): # This might not be the case if we encode an unknown tag and the user didn't supply a count if ((len(payload) < min_count) or (len(payload) > max_count)): raise "Wrong number of arguments supplied to encode tag %s!" % str( tag_num) # Encode the data data = DATA_TYPES[data_type].encode(payload, self.big_endian) # If data type is Digits, pad it with zeroes if (data_type == 15): word_width = DATA_TYPES[data_type].word_width data = ((min_count * word_width) - len(data)) * "0" + data else: raise "Specify either a payload and optionally means to encode, or binary data" return datablock.DataBlock(data=data)
def setTag(self, tag_num, data): """ Set the tag_num to data. """ self.tags[tag_num] = datablock.DataBlock(data=data)