class IPTCObjectData(iptc.IPTCRecord): RECORD_NUM = 8 tags = qdb.QDB() tags.addList("name", ["SubFile"]) tags.addList("num", [10]) tags.addList("count", [None]) tags.addList("data_type", [None])
class GPSIFD(ifd.IFD): # The GPS data. tags = qdb.QDB() # This info is taken from the Exif 2.2 specification, page 46 tags.addList("name", [ "GPSVersionID", "GPSLatitudeRef", "GPSLatitude", "GPSLongitudeRef", "GPSLongitude", "GPSAltitudeRef", "GPSAltitude", "GPSTimeStamp", "GPSSatellites", "GPSStatus", "GPSMeasureMode", "GPSDOP", "GPSSpeedRef", "GPSSpeed", "GPSTrackRef", "GPSTrack", "GPSImgDirectionRef", "GPSImgDirection", "GPSMapDatum", "GPSDestLatitudeRef", "GPSDestLatitude", "GPSDestLongitudeRef", "GPSDestLongitude", "GPSDestBearingRef", "GPSDestBearing", "GPSDestDistanceRef", "GPSDestDistance", "GPSProcessingMethod", "GPSAreaInformation", "GPSDateStamp", "GPSDifferential" ]) tags.addList("num", [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 ]) tags.addList("data_type", [ 1, 2, 5, 2, 5, 1, 5, 5, 2, 2, 2, 5, 2, 5, 2, 5, 2, 5, 2, 2, 5, 2, 5, 2, 5, 2, 5, 7, 7, 2, 3 ]) tags.addList("count", [ 4, 1, 3, 1, 3, 1, 1, 3, False, 1, 1, 1, 1, 1, 1, 1, 1, 1, False, 1, 3, 1, 3, 1, 1, 1, 1, False, False, 11, 1 ])
def __init__(self, fp=None, offset=None, length=None, data=None, big_endian=True): self.big_endian = big_endian # Call the DataBlock constructor datablock.DataBlock.__init__(self, fp=fp, offset=offset, length=length, data=data) # We keep an internal map of all the records self.records = qdb.QDB() self.records.addList("num", [1, 2, 3, 7, 8, 9]) self.records.addList("name", [ "IPTCEnvelope", "IPTCApplication", "IPTCNewsPhoto", "IPTCPreObjectData", "IPTCObjectData", "IPTCPostObjectData" ]) self.records.addList("record", [ IPTCEnvelope(big_endian=big_endian), IPTCApplication(big_endian=big_endian), IPTCNewsPhoto(big_endian=big_endian), IPTCPreObjectData(big_endian=big_endian), IPTCObjectData(big_endian=big_endian), IPTCPostObjectData(big_endian=big_endian) ]) # Remember if we have already parsed the data self.parsed = False
class TiffIFD(ifd.IFD): tags = qdb.QDB() # This info is taken from the Exif 2.2 specification, page 54 tags.addList("name", [ "ImageWidth", "ImageLength", "BitsPerSample", "Compression", "PhotometricInterpretation", "ImageDescription", "Make", "Model", "StripOffsets", "Orientation", "SamplesPerPixel", "RowsPerStrip", "StripByteCounts", "XResolution", "YResolution", "PlanarConfiguration", "ResolutionUnit", "TransferFunction", "Software", "DateTime", "Artist", "WhitePoint", "PrimaryChromaticities", "JPEGInterchangeFormat", "JPEGInterchangeFormatLength", "YCbCrCoefficients", "YCbCrSubSampling", "YCbCrPositioning", "ReferenceBlackWhite", "IPTC-NAA", "Copyright", "Exif IFD Pointer", "GPSInfo IFD Pointer" ]) tags.addList("num", [ 256, 257, 258, 259, 262, 270, 271, 272, 273, 274, 277, 278, 279, 282, 283, 284, 296, 301, 305, 306, 315, 318, 319, 513, 514, 529, 530, 531, 532, 33723, 33432, 34665, 34853 ]) tags.addList( "data_type", [[3, 4], [3, 4], 3, 3, 3, 2, 2, 2, [3, 4], 3, 3, [3, 4], [3, 4], 5, 5, 3, 3, 3, 2, 2, 2, 5, 5, 4, 4, 5, 3, 3, 5, 7, 2, 4, 4]) # -1 means special, False means any. tags.addList("count", [ 1, 1, 3, 1, 1, False, False, False, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, False, 19, False, 2, 6, 1, 1, 3, 2, 1, 6, False, False, 1, 1 ])
class IPTCNewsPhoto(iptc.IPTCRecord): RECORD_NUM = 3 tags = qdb.QDB() tags.addList("name", [ "NewsPhotoVersion", "IPTCPictureNumber", "IPTCImageWidth", "IPTCImageHeight", "IPTCPixelWidth", "IPTCPixelHeight", "SupplementalType", "ColorRepresentation", "InterchangeColorSpace", "ColorSequence", "ICC_Profile", "ColorCalibrationMatrix", "LookupTable", "NumIndexEntries", "ColorPalette", "IPTCBitsPerSample", "SampleStructure", "ScanningDirection", "IPTCImageRotation", "DataCompressionMethod", "QuantizationMethod", "EndPoints", "ExcursionTolerance", "BitsPerComponent", "MaximumDensityRange", "GammaCompensatedValue" ]) tags.addList("num", [ 0, 10, 20, 30, 40, 50, 55, 60, 64, 65, 66, 70, 80, 84, 85, 86, 90, 100, 102, 110, 120, 125, 130, 135, 140, 145 ]) tags.addList("count", [ 1, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]) tags.addList("data_type", [ 3, 2, 3, 3, 3, 3, 1, 3, 1, 1, None, None, None, 3, None, 1, 1, 1, 1, 4, 1, None, 1, 1, 3, 3 ])
class IPTCPostObjectData(iptc.IPTCRecord): RECORD_NUM = 9 tags = qdb.QDB() tags.addList("name", ["ConfirmedObjectSize"]) tags.addList("num", [10]) tags.addList("count", [None]) tags.addList("data_type", [None])
class OlympusIFD(IFDWithHeader): header_str = "OLYMP\x00" header_length = 10 tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", [])
class FoveonIFD(ifd.IFD): header_str = "FOVEON\x00\x00" header_length = 10 tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", [])
class SigmaIFD(ifd.IFD): header_str = "SIGMA\x00\x00\x00" header_length = 10 tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", [])
class IPTCPreObjectData(iptc.IPTCRecord): RECORD_NUM = 7 tags = qdb.QDB() tags.addList("name", [ "SizeMode", "MaxSubfileSize", "ObjectSizeAnnounced", "MaximumObjectSize" ]) tags.addList("num", [10, 20, 90, 95]) tags.addList("count", [None, None, None, None]) tags.addList("data_type", [None, None, None, None])
class IPTCEnvelope(iptc.IPTCRecord): RECORD_NUM = 1 tags = qdb.QDB() tags.addList("name", [ "EnvelopeRecordVersion", "Destination", "FileFormat", "FileVersion", "ServiceIdentifier", "EnvelopeNumber", "ProductID", "EnvelopePriority", "DateSent", "TimeSent", "CodedCharacterSet", "UniqueObjectName", "ARMIdentifier", "ARMVersion" ]) tags.addList("num", [0, 5, 20, 22, 30, 40, 50, 60, 70, 80, 90, 100, 120, 122]) tags.addList( "count", [1, 1, 1, 1, [0, 10], 8, [0, 32], 1, 8, 11, [0, 32], [14, 80], 1, 1]) tags.addList("data_type", [3, 2, 3, 3, 2, 15, 2, 15, 15, 2, 2, 2, 3, 3])
class IPTCApplication(iptc.IPTCRecord): RECORD_NUM = 2 tags = qdb.QDB() tags.addList("name", [ "ApplicationRecordVersion", "ObjectTypeReference", "ObjectAttributeReference", "ObjectName", "EditStatus", "EditorialUpdate", "Urgency", "SubjectReference", "Category", "SupplementalCategories", "FixtureIdentifier", "Keywords", "ContentLocationCode", "ContentLocationName", "ReleaseDate", "ReleaseTime", "ExpirationDate", "ExpirationTime", "SpecialInstructions", "ActionAdvised", "ReferenceService", "ReferenceDate", "ReferenceNumber", "DateCreated", "TimeCreated", "DigitalCreationDate", "DigitalCreationTime", "OriginatingProgram", "ProgramVersion", "ObjectCycle", "By-line", "By-lineTitle", "City", "Sub-location", "Province-State", "Country-PrimaryLocationCode", "Country-PrimaryLocationName", "OriginalTransmissionReference", "Headline", "Credit", "Source", "CopyrightNotice", "Contact", "Caption-Abstract", "LocalCaption", "Writer-Editor", "RasterizedCaption", "ImageType", "ImageOrientation", "LanguageIdentifier", "AudioType", "AudioSamplingRate", "AudioSamplingResolution", "AudioDuration", "AudioOutcue", "JobID", "MasterDocumentID", "ShortDocumentID", "UniqueDocumentID", "OwnerID", "ObjectPreviewFileFormat", "ObjectPreviewFileVersion", "ObjectPreviewData", "ClassifyState", "SimilarityIndex", "DocumentNotes", "DocumentHistory", "ExifCameraInfo" ]) tags.addList("num", [ 0, 3, 4, 5, 7, 8, 10, 12, 15, 20, 22, 25, 26, 27, 30, 35, 37, 38, 40, 42, 45, 47, 50, 55, 60, 62, 63, 65, 70, 75, 80, 85, 90, 92, 95, 100, 101, 103, 105, 110, 115, 116, 118, 120, 121, 122, 125, 130, 131, 135, 150, 151, 152, 153, 154, 184, 185, 186, 187, 188, 200, 201, 202, 225, 228, 230, 231, 232 ]) tags.addList("count", [ 1, [3, 67], [4, 68], [0, 64], [0, 64], 2, 1, [13, 236], [0, 3], [0, 32], [0, 32], [0, 64], 3, [0, 64], 8, 11, 8, 11, [0, 256], 2, [0, 10], 8, 8, 8, 11, 8, 11, [0, 32], [0, 10], 1, [0, 32], [0, 32], [0, 32], [0, 32], [0, 32], 3, [0, 64], [0, 32], [0, 256], [0, 32], [0, 32], [0, 128], [0, 128], [0, 2000], [0, 256], [0, 32], 7360, 2, 1, [2, 3], 2, 6, 2, 6, [0, 64], [0, 64], [0, 256], [0, 64], [0, 128], [0, 128], 0, 1, [0, 256000], [0, 64], [0, 32], [0, 1024], [0, 256], [0, 4096] ]) tags.addList("data_type", [ 3, 2, 2, 2, 2, 15, 15, 2, 2, 2, 2, 2, 2, 2, 15, 2, 15, 2, 2, 15, 2, 15, 15, 15, 2, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2 ])
class PanasonicIFD(ifd.IFD): header_str = "Panasonic\x00\x00\x00" header_length = 12 tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", []) def getBlob(self, offset, next_ifd=0): ret_str = header_str ret_str += ifd.IFD.getBlob( self, offset, None) # Panasonic does not write a next IFD pointer return ret_str def getSize(self): return ifd.IFD.getSize(self, False)
class ExifIFD(ifd.IFD): # The Exif data. tags = qdb.QDB() # This info is taken from the Exif 2.2 specification, page 24 and 25 (with the # exception of the Interoperability IFD Pointer tags.addList("name", [ "ExposureTime", "FNumber", "ExposureProgram", "SpectralSensitivity", "ISOSpeedRatings", "OECF", "ExifVersion", "DateTimeOriginal", "DateTimeDigitized", "ComponentsConfiguration", "CompressedBitsPerPixel", "ShutterSpeedValue", "ApertureValue", "BrightnessValue", "ExposureBiasValue", "MaxApertureValue", "SubjectDistance", "MeteringMode", "LightSource", "Flash", "FocalLength", "SubjectArea", "MakerNote", "UserComment", "SubSecTime", "SubSecTimeOriginal", "SubSecTimeDigitized", "FlashpixVersion", "ColorSpace", "PixelXDimension", "PixelYDimension", "RelatedSoundFile", "Interoperability IFD Pointer", "FlashEnergy", "SpatialFrequencyResponse", "FocalPlaneXResolution", "FocalPlaneYResolution", "FocalPlaneResolutionUnit", "SubjectLocation", "ExposureIndex", "SensingMethod", "FileSource", "SceneType", "CFAPattern", "CustomRendered", "ExposureMode", "WhiteBalance", "DigitalZoomRatio", "FocalLengthIn35mmFilm", "SceneCaptureType", "GainControl", "Contrast", "Saturation", "Sharpness", "DeviceSettingDescription", "SubjectDistanceRange", "ImageUniqueID" ]) tags.addList("num", [ 33434, 33437, 34850, 34852, 34855, 34856, 36864, 36867, 36868, 37121, 37122, 37377, 37378, 37379, 37380, 37381, 37382, 37383, 37384, 37385, 37386, 37396, 37500, 37510, 37520, 37521, 37522, 40960, 40961, 40962, 40963, 40964, 40965, 41483, 41484, 41486, 41487, 41488, 41492, 41493, 41495, 41728, 41729, 41730, 41985, 41986, 41987, 41988, 41989, 41990, 41991, 41992, 41993, 41994, 41995, 41996, 42016 ]) tags.addList("data_type", [ 5, 5, 3, 2, 3, 7, 7, 2, 2, 7, 5, 10, 5, 10, 10, 5, 5, 3, 3, 3, 5, 3, 7, 7, 2, 2, 2, 7, 3, [3, 4], [3, 4], 2, 4, 5, 7, 5, 5, 3, 3, 5, 3, 7, 7, 7, 3, 3, 3, 5, 3, 3, 5, 3, 3, 3, 7, 3, 2 ]) tags.addList("count", [ 1, 1, 1, False, False, False, 4, 19, 19, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, False, False, False, False, False, 4, 1, 1, 1, 12, 1, 1, False, 1, 1, 1, 2, 1, 1, 1, 1, False, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, False, 1, 32 ])
class MinoltaIFD(ifd.IFD): tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", []) def __init__(self, file_pointer=None, ifd_offset=0, header_offset=0, data=None, big_endian=True): # Minolta always uses big endian ifd.IFD.__init__(self, file_pointer, ifd_offset, header_offset, data, big_endian=True)
def __init__(self, ifd_offset=0, header_offset=0, fp=None, length=None, data=None, big_endian=True): """ Initialize the Exif data with the ifd_offset of the IFD in the containing structure (like the segment), the header_offset to the containing structure in the file or data buffer, pointer to an open file of data buffer, and optionally the length of the actual data. The big_endian parameter specifies whther the data is in big endian format. """ # We save both the header offset (to the containing structure) and the # ifd offset (in the containing structure). Since all offsets are specified # relative to the start of the containing structure, we use this as the # start for the DataBlock. This way, all reading and seeking will be # correct. self.ifd_offset = ifd_offset self.header_offset = header_offset self.big_endian = big_endian # Call the DataBlock constructor datablock.DataBlock.__init__(self, fp=fp, offset=header_offset, data=data) # Create the database of segment data self.records = qdb.QDB() # The segment numbers, although Exif does not have an official numbering self.records.addList("num", [1, 2, 3, 4, 5, 6]) # The segment names self.records.addList( "name", ["tiff", "exif", "gps", "interop", "makernote", "ifd1"]) # The individual IFD's. None means not loaded. self.records.addList("record", [None, None, None, None, None, None])
class FujifilmIFD(ifd.IFD): tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", []) 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 getBlob(self, offset, next_ifd=0): ret_str = "FUJIFILM" ret_str += byteform.itob(12, 4, big_endian=self.big_endian) ret_str += ifd.IFD.getBlob(self, 12, next_ifd) return ret_str
class CanonIFD(ifd.IFD): tags = qdb.QDB() tags.addList("name", []) tags.addList("num", []) tags.addList("data_type", []) tags.addList("count", [])