Blackened everything

This commit is contained in:
Joel Collins 2019-09-15 14:17:52 +01:00
parent e213647217
commit 5966ce29be
57 changed files with 1938 additions and 1414 deletions

View file

@ -7,5 +7,4 @@ from ._exif import *
from ._exceptions import *
VERSION = '1.1.2'
VERSION = "1.1.2"

View file

@ -12,20 +12,21 @@ def split_into_segments(data):
head = 2
segments = [b"\xff\xd8"]
while 1:
if data[head: head + 2] == b"\xff\xda":
if data[head : head + 2] == b"\xff\xda":
segments.append(data[head:])
break
else:
length = struct.unpack(">H", data[head + 2: head + 4])[0]
length = struct.unpack(">H", data[head + 2 : head + 4])[0]
endPoint = head + length + 2
seg = data[head: endPoint]
seg = data[head:endPoint]
segments.append(seg)
head = endPoint
if (head >= len(data)):
if head >= len(data):
raise InvalidImageDataError("Wrong JPEG data.")
return segments
def read_exif_from_file(filename):
"""Slices JPEG meta data into a list from JPEG binary data.
"""
@ -39,11 +40,11 @@ def read_exif_from_file(filename):
HEAD_LENGTH = 4
exif = None
while 1:
length = struct.unpack(">H", head[2: 4])[0]
length = struct.unpack(">H", head[2:4])[0]
if head[:2] == b"\xff\xe1":
segment_data = f.read(length - 2)
if segment_data[:4] != b'Exif':
if segment_data[:4] != b"Exif":
head = f.read(HEAD_LENGTH)
continue
exif = head + segment_data
@ -57,6 +58,7 @@ def read_exif_from_file(filename):
f.close()
return exif
def get_exif_seg(segments):
"""Returns Exif from JPEG meta data list
"""
@ -69,9 +71,11 @@ def get_exif_seg(segments):
def merge_segments(segments, exif=b""):
"""Merges Exif with APP0 and APP1 manipulations.
"""
if segments[1][0:2] == b"\xff\xe0" and \
segments[2][0:2] == b"\xff\xe1" and \
segments[2][4:10] == b"Exif\x00\x00":
if (
segments[1][0:2] == b"\xff\xe0"
and segments[2][0:2] == b"\xff\xe1"
and segments[2][4:10] == b"Exif\x00\x00"
):
if exif:
segments[2] = exif
segments.pop(1)
@ -82,8 +86,7 @@ def merge_segments(segments, exif=b""):
elif segments[1][0:2] == b"\xff\xe0":
if exif:
segments[1] = exif
elif segments[1][0:2] == b"\xff\xe1" and \
segments[1][4:10] == b"Exif\x00\x00":
elif segments[1][0:2] == b"\xff\xe1" and segments[1][4:10] == b"Exif\x00\x00":
if exif:
segments[1] = exif
elif exif is None:

View file

@ -31,16 +31,20 @@ def dump(exif_dict_original):
else:
zeroth_ifd = {}
if (("Exif" in exif_dict) and len(exif_dict["Exif"]) or
("Interop" in exif_dict) and len(exif_dict["Interop"]) ):
if (
("Exif" in exif_dict)
and len(exif_dict["Exif"])
or ("Interop" in exif_dict)
and len(exif_dict["Interop"])
):
zeroth_ifd[ImageIFD.ExifTag] = 1
exif_is = True
exif_ifd = exif_dict["Exif"]
if ("Interop" in exif_dict) and len(exif_dict["Interop"]):
exif_ifd[ExifIFD. InteroperabilityTag] = 1
exif_ifd[ExifIFD.InteroperabilityTag] = 1
interop_is = True
interop_ifd = exif_dict["Interop"]
elif ExifIFD. InteroperabilityTag in exif_ifd:
elif ExifIFD.InteroperabilityTag in exif_ifd:
exif_ifd.pop(ExifIFD.InteroperabilityTag)
elif ImageIFD.ExifTag in zeroth_ifd:
zeroth_ifd.pop(ImageIFD.ExifTag)
@ -52,17 +56,20 @@ def dump(exif_dict_original):
elif ImageIFD.GPSTag in zeroth_ifd:
zeroth_ifd.pop(ImageIFD.GPSTag)
if (("1st" in exif_dict) and
("thumbnail" in exif_dict) and
(exif_dict["thumbnail"] is not None)):
if (
("1st" in exif_dict)
and ("thumbnail" in exif_dict)
and (exif_dict["thumbnail"] is not None)
):
first_is = True
exif_dict["1st"][ImageIFD.JPEGInterchangeFormat] = 1
exif_dict["1st"][ImageIFD.JPEGInterchangeFormatLength] = 1
first_ifd = exif_dict["1st"]
zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0)
zeroth_length = (len(zeroth_set[0]) + exif_is * 12 + gps_is * 12 + 4 +
len(zeroth_set[1]))
zeroth_length = (
len(zeroth_set[0]) + exif_is * 12 + gps_is * 12 + 4 + len(zeroth_set[1])
)
if exif_is:
exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length)
@ -115,8 +122,7 @@ def dump(exif_dict_original):
else:
gps_pointer = b""
if interop_is:
pointer_value = (TIFF_HEADER_LENGTH +
zeroth_length + exif_length + gps_length)
pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length + gps_length
pointer_str = struct.pack(">I", pointer_value)
key = ExifIFD.InteroperabilityTag
key_str = struct.pack(">H", key)
@ -126,33 +132,46 @@ def dump(exif_dict_original):
else:
interop_pointer = b""
if first_is:
pointer_value = (TIFF_HEADER_LENGTH + zeroth_length +
exif_length + gps_length + interop_length)
pointer_value = (
TIFF_HEADER_LENGTH
+ zeroth_length
+ exif_length
+ gps_length
+ interop_length
)
first_ifd_pointer = struct.pack(">L", pointer_value)
thumbnail_pointer = (pointer_value + len(first_set[0]) + 24 +
4 + len(first_set[1]))
thumbnail_p_bytes = (b"\x02\x01\x00\x04\x00\x00\x00\x01" +
struct.pack(">L", thumbnail_pointer))
thumbnail_length_bytes = (b"\x02\x02\x00\x04\x00\x00\x00\x01" +
struct.pack(">L", len(thumbnail)))
first_bytes = (first_set[0] + thumbnail_p_bytes +
thumbnail_length_bytes + b"\x00\x00\x00\x00" +
first_set[1] + thumbnail)
thumbnail_pointer = (
pointer_value + len(first_set[0]) + 24 + 4 + len(first_set[1])
)
thumbnail_p_bytes = b"\x02\x01\x00\x04\x00\x00\x00\x01" + struct.pack(
">L", thumbnail_pointer
)
thumbnail_length_bytes = b"\x02\x02\x00\x04\x00\x00\x00\x01" + struct.pack(
">L", len(thumbnail)
)
first_bytes = (
first_set[0]
+ thumbnail_p_bytes
+ thumbnail_length_bytes
+ b"\x00\x00\x00\x00"
+ first_set[1]
+ thumbnail
)
else:
first_ifd_pointer = b"\x00\x00\x00\x00"
zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer +
first_ifd_pointer + zeroth_set[1])
zeroth_bytes = (
zeroth_set[0] + exif_pointer + gps_pointer + first_ifd_pointer + zeroth_set[1]
)
if exif_is:
exif_bytes = exif_set[0] + interop_pointer + exif_set[1]
return (header + zeroth_bytes + exif_bytes + gps_bytes +
interop_bytes + first_bytes)
return header + zeroth_bytes + exif_bytes + gps_bytes + interop_bytes + first_bytes
def _get_thumbnail(jpeg):
segments = split_into_segments(jpeg)
while (b"\xff\xe0" <= segments[1][0:2] <= b"\xff\xef"):
while b"\xff\xe0" <= segments[1][0:2] <= b"\xff\xef":
segments.pop(1)
thumbnail = b"".join(segments)
return thumbnail
@ -161,24 +180,31 @@ def _get_thumbnail(jpeg):
def _pack_byte(*args):
return struct.pack("B" * len(args), *args)
def _pack_signed_byte(*args):
return struct.pack("b" * len(args), *args)
def _pack_short(*args):
return struct.pack(">" + "H" * len(args), *args)
def _pack_signed_short(*args):
return struct.pack(">" + "h" * len(args), *args)
def _pack_long(*args):
return struct.pack(">" + "L" * len(args), *args)
def _pack_slong(*args):
return struct.pack(">" + "l" * len(args), *args)
def _pack_float(*args):
return struct.pack(">" + "f" * len(args), *args)
def _pack_double(*args):
return struct.pack(">" + "d" * len(args), *args)
@ -190,16 +216,14 @@ def _value_to_bytes(raw_value, value_type, offset):
if value_type == TYPES.Byte:
length = len(raw_value)
if length <= 4:
value_str = (_pack_byte(*raw_value) +
b"\x00" * (4 - length))
value_str = _pack_byte(*raw_value) + b"\x00" * (4 - length)
else:
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_byte(*raw_value)
elif value_type == TYPES.Short:
length = len(raw_value)
if length <= 2:
value_str = (_pack_short(*raw_value) +
b"\x00\x00" * (2 - length))
value_str = _pack_short(*raw_value) + b"\x00\x00" * (2 - length)
else:
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_short(*raw_value)
@ -241,8 +265,7 @@ def _value_to_bytes(raw_value, value_type, offset):
new_value = b""
for n, val in enumerate(raw_value):
num, den = val
new_value += (struct.pack(">L", num) +
struct.pack(">L", den))
new_value += struct.pack(">L", num) + struct.pack(">L", den)
value_str = struct.pack(">I", offset)
four_bytes_over = new_value
elif value_type == TYPES.SRational:
@ -255,8 +278,7 @@ def _value_to_bytes(raw_value, value_type, offset):
new_value = b""
for n, val in enumerate(raw_value):
num, den = val
new_value += (struct.pack(">l", num) +
struct.pack(">l", den))
new_value += struct.pack(">l", num) + struct.pack(">l", den)
value_str = struct.pack(">I", offset)
four_bytes_over = new_value
elif value_type == TYPES.Undefined:
@ -272,19 +294,17 @@ def _value_to_bytes(raw_value, value_type, offset):
value_str = raw_value + b"\x00" * (4 - length)
except TypeError:
raise ValueError("Got invalid type to convert.")
elif value_type == TYPES.SByte: # Signed Byte
elif value_type == TYPES.SByte: # Signed Byte
length = len(raw_value)
if length <= 4:
value_str = (_pack_signed_byte(*raw_value) +
b"\x00" * (4 - length))
value_str = _pack_signed_byte(*raw_value) + b"\x00" * (4 - length)
else:
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_signed_byte(*raw_value)
elif value_type == TYPES.SShort: # Signed Short
elif value_type == TYPES.SShort: # Signed Short
length = len(raw_value)
if length <= 2:
value_str = (_pack_signed_short(*raw_value) +
b"\x00\x00" * (2 - length))
value_str = _pack_signed_short(*raw_value) + b"\x00\x00" * (2 - length)
else:
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_signed_short(*raw_value)
@ -295,7 +315,7 @@ def _value_to_bytes(raw_value, value_type, offset):
else:
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_float(*raw_value)
elif value_type == TYPES.DFloat: # Double
elif value_type == TYPES.DFloat: # Double
length = len(raw_value)
value_str = struct.pack(">I", offset)
four_bytes_over = _pack_double(*raw_value)
@ -303,6 +323,7 @@ def _value_to_bytes(raw_value, value_type, offset):
length_str = struct.pack(">I", length)
return length_str, value_str, four_bytes_over
def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
tag_count = len(ifd_dict)
entry_header = struct.pack(">H", tag_count)
@ -318,7 +339,10 @@ def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
continue
elif (ifd == "Exif") and (key == ExifIFD.InteroperabilityTag):
continue
elif (ifd == "1st") and (key in (ImageIFD.JPEGInterchangeFormat, ImageIFD.JPEGInterchangeFormatLength)):
elif (ifd == "1st") and (
key
in (ImageIFD.JPEGInterchangeFormat, ImageIFD.JPEGInterchangeFormatLength)
):
continue
raw_value = ifd_dict[key]
@ -332,13 +356,13 @@ def _dict_to_bytes(ifd_dict, ifd, ifd_offset):
offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + len(values)
try:
length_str, value_str, four_bytes_over = _value_to_bytes(raw_value,
value_type,
offset)
length_str, value_str, four_bytes_over = _value_to_bytes(
raw_value, value_type, offset
)
except ValueError:
raise ValueError(
'"dump" got wrong type of exif value.\n' +
'{0} in {1} IFD. Got as {2}.'.format(key, ifd, type(ifd_dict[key]))
'"dump" got wrong type of exif value.\n'
+ "{0} in {1} IFD. Got as {2}.".format(key, ifd, type(ifd_dict[key]))
)
entries += key_str + type_str + length_str + value_str

View file

@ -14,314 +14,322 @@ class TYPES:
TAGS = {
'Image': {11: {'name': 'ProcessingSoftware', 'type': TYPES.Ascii},
254: {'name': 'NewSubfileType', 'type': TYPES.Long},
255: {'name': 'SubfileType', 'type': TYPES.Short},
256: {'name': 'ImageWidth', 'type': TYPES.Long},
257: {'name': 'ImageLength', 'type': TYPES.Long},
258: {'name': 'BitsPerSample', 'type': TYPES.Short},
259: {'name': 'Compression', 'type': TYPES.Short},
262: {'name': 'PhotometricInterpretation', 'type': TYPES.Short},
263: {'name': 'Threshholding', 'type': TYPES.Short},
264: {'name': 'CellWidth', 'type': TYPES.Short},
265: {'name': 'CellLength', 'type': TYPES.Short},
266: {'name': 'FillOrder', 'type': TYPES.Short},
269: {'name': 'DocumentName', 'type': TYPES.Ascii},
270: {'name': 'ImageDescription', 'type': TYPES.Ascii},
271: {'name': 'Make', 'type': TYPES.Ascii},
272: {'name': 'Model', 'type': TYPES.Ascii},
273: {'name': 'StripOffsets', 'type': TYPES.Long},
274: {'name': 'Orientation', 'type': TYPES.Short},
277: {'name': 'SamplesPerPixel', 'type': TYPES.Short},
278: {'name': 'RowsPerStrip', 'type': TYPES.Long},
279: {'name': 'StripByteCounts', 'type': TYPES.Long},
282: {'name': 'XResolution', 'type': TYPES.Rational},
283: {'name': 'YResolution', 'type': TYPES.Rational},
284: {'name': 'PlanarConfiguration', 'type': TYPES.Short},
290: {'name': 'GrayResponseUnit', 'type': TYPES.Short},
291: {'name': 'GrayResponseCurve', 'type': TYPES.Short},
292: {'name': 'T4Options', 'type': TYPES.Long},
293: {'name': 'T6Options', 'type': TYPES.Long},
296: {'name': 'ResolutionUnit', 'type': TYPES.Short},
301: {'name': 'TransferFunction', 'type': TYPES.Short},
305: {'name': 'Software', 'type': TYPES.Ascii},
306: {'name': 'DateTime', 'type': TYPES.Ascii},
315: {'name': 'Artist', 'type': TYPES.Ascii},
316: {'name': 'HostComputer', 'type': TYPES.Ascii},
317: {'name': 'Predictor', 'type': TYPES.Short},
318: {'name': 'WhitePoint', 'type': TYPES.Rational},
319: {'name': 'PrimaryChromaticities', 'type': TYPES.Rational},
320: {'name': 'ColorMap', 'type': TYPES.Short},
321: {'name': 'HalftoneHints', 'type': TYPES.Short},
322: {'name': 'TileWidth', 'type': TYPES.Short},
323: {'name': 'TileLength', 'type': TYPES.Short},
324: {'name': 'TileOffsets', 'type': TYPES.Short},
325: {'name': 'TileByteCounts', 'type': TYPES.Short},
330: {'name': 'SubIFDs', 'type': TYPES.Long},
332: {'name': 'InkSet', 'type': TYPES.Short},
333: {'name': 'InkNames', 'type': TYPES.Ascii},
334: {'name': 'NumberOfInks', 'type': TYPES.Short},
336: {'name': 'DotRange', 'type': TYPES.Byte},
337: {'name': 'TargetPrinter', 'type': TYPES.Ascii},
338: {'name': 'ExtraSamples', 'type': TYPES.Short},
339: {'name': 'SampleFormat', 'type': TYPES.Short},
340: {'name': 'SMinSampleValue', 'type': TYPES.Short},
341: {'name': 'SMaxSampleValue', 'type': TYPES.Short},
342: {'name': 'TransferRange', 'type': TYPES.Short},
343: {'name': 'ClipPath', 'type': TYPES.Byte},
344: {'name': 'XClipPathUnits', 'type': TYPES.Long},
345: {'name': 'YClipPathUnits', 'type': TYPES.Long},
346: {'name': 'Indexed', 'type': TYPES.Short},
347: {'name': 'JPEGTables', 'type': TYPES.Undefined},
351: {'name': 'OPIProxy', 'type': TYPES.Short},
512: {'name': 'JPEGProc', 'type': TYPES.Long},
513: {'name': 'JPEGInterchangeFormat', 'type': TYPES.Long},
514: {'name': 'JPEGInterchangeFormatLength', 'type': TYPES.Long},
515: {'name': 'JPEGRestartInterval', 'type': TYPES.Short},
517: {'name': 'JPEGLosslessPredictors', 'type': TYPES.Short},
518: {'name': 'JPEGPointTransforms', 'type': TYPES.Short},
519: {'name': 'JPEGQTables', 'type': TYPES.Long},
520: {'name': 'JPEGDCTables', 'type': TYPES.Long},
521: {'name': 'JPEGACTables', 'type': TYPES.Long},
529: {'name': 'YCbCrCoefficients', 'type': TYPES.Rational},
530: {'name': 'YCbCrSubSampling', 'type': TYPES.Short},
531: {'name': 'YCbCrPositioning', 'type': TYPES.Short},
532: {'name': 'ReferenceBlackWhite', 'type': TYPES.Rational},
700: {'name': 'XMLPacket', 'type': TYPES.Byte},
18246: {'name': 'Rating', 'type': TYPES.Short},
18249: {'name': 'RatingPercent', 'type': TYPES.Short},
32781: {'name': 'ImageID', 'type': TYPES.Ascii},
33421: {'name': 'CFARepeatPatternDim', 'type': TYPES.Short},
33422: {'name': 'CFAPattern', 'type': TYPES.Byte},
33423: {'name': 'BatteryLevel', 'type': TYPES.Rational},
33432: {'name': 'Copyright', 'type': TYPES.Ascii},
33434: {'name': 'ExposureTime', 'type': TYPES.Rational},
34377: {'name': 'ImageResources', 'type': TYPES.Byte},
34665: {'name': 'ExifTag', 'type': TYPES.Long},
34675: {'name': 'InterColorProfile', 'type': TYPES.Undefined},
34853: {'name': 'GPSTag', 'type': TYPES.Long},
34857: {'name': 'Interlace', 'type': TYPES.Short},
34858: {'name': 'TimeZoneOffset', 'type': TYPES.Long},
34859: {'name': 'SelfTimerMode', 'type': TYPES.Short},
37387: {'name': 'FlashEnergy', 'type': TYPES.Rational},
37388: {'name': 'SpatialFrequencyResponse', 'type': TYPES.Undefined},
37389: {'name': 'Noise', 'type': TYPES.Undefined},
37390: {'name': 'FocalPlaneXResolution', 'type': TYPES.Rational},
37391: {'name': 'FocalPlaneYResolution', 'type': TYPES.Rational},
37392: {'name': 'FocalPlaneResolutionUnit', 'type': TYPES.Short},
37393: {'name': 'ImageNumber', 'type': TYPES.Long},
37394: {'name': 'SecurityClassification', 'type': TYPES.Ascii},
37395: {'name': 'ImageHistory', 'type': TYPES.Ascii},
37397: {'name': 'ExposureIndex', 'type': TYPES.Rational},
37398: {'name': 'TIFFEPStandardID', 'type': TYPES.Byte},
37399: {'name': 'SensingMethod', 'type': TYPES.Short},
40091: {'name': 'XPTitle', 'type': TYPES.Byte},
40092: {'name': 'XPComment', 'type': TYPES.Byte},
40093: {'name': 'XPAuthor', 'type': TYPES.Byte},
40094: {'name': 'XPKeywords', 'type': TYPES.Byte},
40095: {'name': 'XPSubject', 'type': TYPES.Byte},
50341: {'name': 'PrintImageMatching', 'type': TYPES.Undefined},
50706: {'name': 'DNGVersion', 'type': TYPES.Byte},
50707: {'name': 'DNGBackwardVersion', 'type': TYPES.Byte},
50708: {'name': 'UniqueCameraModel', 'type': TYPES.Ascii},
50709: {'name': 'LocalizedCameraModel', 'type': TYPES.Byte},
50710: {'name': 'CFAPlaneColor', 'type': TYPES.Byte},
50711: {'name': 'CFALayout', 'type': TYPES.Short},
50712: {'name': 'LinearizationTable', 'type': TYPES.Short},
50713: {'name': 'BlackLevelRepeatDim', 'type': TYPES.Short},
50714: {'name': 'BlackLevel', 'type': TYPES.Rational},
50715: {'name': 'BlackLevelDeltaH', 'type': TYPES.SRational},
50716: {'name': 'BlackLevelDeltaV', 'type': TYPES.SRational},
50717: {'name': 'WhiteLevel', 'type': TYPES.Short},
50718: {'name': 'DefaultScale', 'type': TYPES.Rational},
50719: {'name': 'DefaultCropOrigin', 'type': TYPES.Short},
50720: {'name': 'DefaultCropSize', 'type': TYPES.Short},
50721: {'name': 'ColorMatrix1', 'type': TYPES.SRational},
50722: {'name': 'ColorMatrix2', 'type': TYPES.SRational},
50723: {'name': 'CameraCalibration1', 'type': TYPES.SRational},
50724: {'name': 'CameraCalibration2', 'type': TYPES.SRational},
50725: {'name': 'ReductionMatrix1', 'type': TYPES.SRational},
50726: {'name': 'ReductionMatrix2', 'type': TYPES.SRational},
50727: {'name': 'AnalogBalance', 'type': TYPES.Rational},
50728: {'name': 'AsShotNeutral', 'type': TYPES.Short},
50729: {'name': 'AsShotWhiteXY', 'type': TYPES.Rational},
50730: {'name': 'BaselineExposure', 'type': TYPES.SRational},
50731: {'name': 'BaselineNoise', 'type': TYPES.Rational},
50732: {'name': 'BaselineSharpness', 'type': TYPES.Rational},
50733: {'name': 'BayerGreenSplit', 'type': TYPES.Long},
50734: {'name': 'LinearResponseLimit', 'type': TYPES.Rational},
50735: {'name': 'CameraSerialNumber', 'type': TYPES.Ascii},
50736: {'name': 'LensInfo', 'type': TYPES.Rational},
50737: {'name': 'ChromaBlurRadius', 'type': TYPES.Rational},
50738: {'name': 'AntiAliasStrength', 'type': TYPES.Rational},
50739: {'name': 'ShadowScale', 'type': TYPES.SRational},
50740: {'name': 'DNGPrivateData', 'type': TYPES.Byte},
50741: {'name': 'MakerNoteSafety', 'type': TYPES.Short},
50778: {'name': 'CalibrationIlluminant1', 'type': TYPES.Short},
50779: {'name': 'CalibrationIlluminant2', 'type': TYPES.Short},
50780: {'name': 'BestQualityScale', 'type': TYPES.Rational},
50781: {'name': 'RawDataUniqueID', 'type': TYPES.Byte},
50827: {'name': 'OriginalRawFileName', 'type': TYPES.Byte},
50828: {'name': 'OriginalRawFileData', 'type': TYPES.Undefined},
50829: {'name': 'ActiveArea', 'type': TYPES.Short},
50830: {'name': 'MaskedAreas', 'type': TYPES.Short},
50831: {'name': 'AsShotICCProfile', 'type': TYPES.Undefined},
50832: {'name': 'AsShotPreProfileMatrix', 'type': TYPES.SRational},
50833: {'name': 'CurrentICCProfile', 'type': TYPES.Undefined},
50834: {'name': 'CurrentPreProfileMatrix', 'type': TYPES.SRational},
50879: {'name': 'ColorimetricReference', 'type': TYPES.Short},
50931: {'name': 'CameraCalibrationSignature', 'type': TYPES.Byte},
50932: {'name': 'ProfileCalibrationSignature', 'type': TYPES.Byte},
50934: {'name': 'AsShotProfileName', 'type': TYPES.Byte},
50935: {'name': 'NoiseReductionApplied', 'type': TYPES.Rational},
50936: {'name': 'ProfileName', 'type': TYPES.Byte},
50937: {'name': 'ProfileHueSatMapDims', 'type': TYPES.Long},
50938: {'name': 'ProfileHueSatMapData1', 'type': TYPES.Float},
50939: {'name': 'ProfileHueSatMapData2', 'type': TYPES.Float},
50940: {'name': 'ProfileToneCurve', 'type': TYPES.Float},
50941: {'name': 'ProfileEmbedPolicy', 'type': TYPES.Long},
50942: {'name': 'ProfileCopyright', 'type': TYPES.Byte},
50964: {'name': 'ForwardMatrix1', 'type': TYPES.SRational},
50965: {'name': 'ForwardMatrix2', 'type': TYPES.SRational},
50966: {'name': 'PreviewApplicationName', 'type': TYPES.Byte},
50967: {'name': 'PreviewApplicationVersion', 'type': TYPES.Byte},
50968: {'name': 'PreviewSettingsName', 'type': TYPES.Byte},
50969: {'name': 'PreviewSettingsDigest', 'type': TYPES.Byte},
50970: {'name': 'PreviewColorSpace', 'type': TYPES.Long},
50971: {'name': 'PreviewDateTime', 'type': TYPES.Ascii},
50972: {'name': 'RawImageDigest', 'type': TYPES.Undefined},
50973: {'name': 'OriginalRawFileDigest', 'type': TYPES.Undefined},
50974: {'name': 'SubTileBlockSize', 'type': TYPES.Long},
50975: {'name': 'RowInterleaveFactor', 'type': TYPES.Long},
50981: {'name': 'ProfileLookTableDims', 'type': TYPES.Long},
50982: {'name': 'ProfileLookTableData', 'type': TYPES.Float},
51008: {'name': 'OpcodeList1', 'type': TYPES.Undefined},
51009: {'name': 'OpcodeList2', 'type': TYPES.Undefined},
51022: {'name': 'OpcodeList3', 'type': TYPES.Undefined},
60606: {'name': 'ZZZTestSlong1', 'type': TYPES.SLong},
60607: {'name': 'ZZZTestSlong2', 'type': TYPES.SLong},
60608: {'name': 'ZZZTestSByte', 'type': TYPES.SByte},
60609: {'name': 'ZZZTestSShort', 'type': TYPES.SShort},
60610: {'name': 'ZZZTestDFloat', 'type': TYPES.DFloat},},
'Exif': {33434: {'name': 'ExposureTime', 'type': TYPES.Rational},
33437: {'name': 'FNumber', 'type': TYPES.Rational},
34850: {'name': 'ExposureProgram', 'type': TYPES.Short},
34852: {'name': 'SpectralSensitivity', 'type': TYPES.Ascii},
34855: {'name': 'ISOSpeedRatings', 'type': TYPES.Short},
34856: {'name': 'OECF', 'type': TYPES.Undefined},
34864: {'name': 'SensitivityType', 'type': TYPES.Short},
34865: {'name': 'StandardOutputSensitivity', 'type': TYPES.Long},
34866: {'name': 'RecommendedExposureIndex', 'type': TYPES.Long},
34867: {'name': 'ISOSpeed', 'type': TYPES.Long},
34868: {'name': 'ISOSpeedLatitudeyyy', 'type': TYPES.Long},
34869: {'name': 'ISOSpeedLatitudezzz', 'type': TYPES.Long},
36864: {'name': 'ExifVersion', 'type': TYPES.Undefined},
36867: {'name': 'DateTimeOriginal', 'type': TYPES.Ascii},
36868: {'name': 'DateTimeDigitized', 'type': TYPES.Ascii},
36880: {'name': 'OffsetTime', 'type': TYPES.Ascii},
36881: {'name': 'OffsetTimeOriginal', 'type': TYPES.Ascii},
36882: {'name': 'OffsetTimeDigitized', 'type': TYPES.Ascii},
37121: {'name': 'ComponentsConfiguration', 'type': TYPES.Undefined},
37122: {'name': 'CompressedBitsPerPixel', 'type': TYPES.Rational},
37377: {'name': 'ShutterSpeedValue', 'type': TYPES.SRational},
37378: {'name': 'ApertureValue', 'type': TYPES.Rational},
37379: {'name': 'BrightnessValue', 'type': TYPES.SRational},
37380: {'name': 'ExposureBiasValue', 'type': TYPES.SRational},
37381: {'name': 'MaxApertureValue', 'type': TYPES.Rational},
37382: {'name': 'SubjectDistance', 'type': TYPES.Rational},
37383: {'name': 'MeteringMode', 'type': TYPES.Short},
37384: {'name': 'LightSource', 'type': TYPES.Short},
37385: {'name': 'Flash', 'type': TYPES.Short},
37386: {'name': 'FocalLength', 'type': TYPES.Rational},
37396: {'name': 'SubjectArea', 'type': TYPES.Short},
37500: {'name': 'MakerNote', 'type': TYPES.Undefined},
37510: {'name': 'UserComment', 'type': TYPES.Undefined},
37520: {'name': 'SubSecTime', 'type': TYPES.Ascii},
37521: {'name': 'SubSecTimeOriginal', 'type': TYPES.Ascii},
37522: {'name': 'SubSecTimeDigitized', 'type': TYPES.Ascii},
37888: {'name': 'Temperature', 'type': TYPES.SRational},
37889: {'name': 'Humidity', 'type': TYPES.Rational},
37890: {'name': 'Pressure', 'type': TYPES.Rational},
37891: {'name': 'WaterDepth', 'type': TYPES.SRational},
37892: {'name': 'Acceleration', 'type': TYPES.Rational},
37893: {'name': 'CameraElevationAngle', 'type': TYPES.SRational},
40960: {'name': 'FlashpixVersion', 'type': TYPES.Undefined},
40961: {'name': 'ColorSpace', 'type': TYPES.Short},
40962: {'name': 'PixelXDimension', 'type': TYPES.Long},
40963: {'name': 'PixelYDimension', 'type': TYPES.Long},
40964: {'name': 'RelatedSoundFile', 'type': TYPES.Ascii},
40965: {'name': 'InteroperabilityTag', 'type': TYPES.Long},
41483: {'name': 'FlashEnergy', 'type': TYPES.Rational},
41484: {'name': 'SpatialFrequencyResponse', 'type': TYPES.Undefined},
41486: {'name': 'FocalPlaneXResolution', 'type': TYPES.Rational},
41487: {'name': 'FocalPlaneYResolution', 'type': TYPES.Rational},
41488: {'name': 'FocalPlaneResolutionUnit', 'type': TYPES.Short},
41492: {'name': 'SubjectLocation', 'type': TYPES.Short},
41493: {'name': 'ExposureIndex', 'type': TYPES.Rational},
41495: {'name': 'SensingMethod', 'type': TYPES.Short},
41728: {'name': 'FileSource', 'type': TYPES.Undefined},
41729: {'name': 'SceneType', 'type': TYPES.Undefined},
41730: {'name': 'CFAPattern', 'type': TYPES.Undefined},
41985: {'name': 'CustomRendered', 'type': TYPES.Short},
41986: {'name': 'ExposureMode', 'type': TYPES.Short},
41987: {'name': 'WhiteBalance', 'type': TYPES.Short},
41988: {'name': 'DigitalZoomRatio', 'type': TYPES.Rational},
41989: {'name': 'FocalLengthIn35mmFilm', 'type': TYPES.Short},
41990: {'name': 'SceneCaptureType', 'type': TYPES.Short},
41991: {'name': 'GainControl', 'type': TYPES.Short},
41992: {'name': 'Contrast', 'type': TYPES.Short},
41993: {'name': 'Saturation', 'type': TYPES.Short},
41994: {'name': 'Sharpness', 'type': TYPES.Short},
41995: {'name': 'DeviceSettingDescription', 'type': TYPES.Undefined},
41996: {'name': 'SubjectDistanceRange', 'type': TYPES.Short},
42016: {'name': 'ImageUniqueID', 'type': TYPES.Ascii},
42032: {'name': 'CameraOwnerName', 'type': TYPES.Ascii},
42033: {'name': 'BodySerialNumber', 'type': TYPES.Ascii},
42034: {'name': 'LensSpecification', 'type': TYPES.Rational},
42035: {'name': 'LensMake', 'type': TYPES.Ascii},
42036: {'name': 'LensModel', 'type': TYPES.Ascii},
42037: {'name': 'LensSerialNumber', 'type': TYPES.Ascii},
42240: {'name': 'Gamma', 'type': TYPES.Rational}},
'GPS': {0: {'name': 'GPSVersionID', 'type': TYPES.Byte},
1: {'name': 'GPSLatitudeRef', 'type': TYPES.Ascii},
2: {'name': 'GPSLatitude', 'type': TYPES.Rational},
3: {'name': 'GPSLongitudeRef', 'type': TYPES.Ascii},
4: {'name': 'GPSLongitude', 'type': TYPES.Rational},
5: {'name': 'GPSAltitudeRef', 'type': TYPES.Byte},
6: {'name': 'GPSAltitude', 'type': TYPES.Rational},
7: {'name': 'GPSTimeStamp', 'type': TYPES.Rational},
8: {'name': 'GPSSatellites', 'type': TYPES.Ascii},
9: {'name': 'GPSStatus', 'type': TYPES.Ascii},
10: {'name': 'GPSMeasureMode', 'type': TYPES.Ascii},
11: {'name': 'GPSDOP', 'type': TYPES.Rational},
12: {'name': 'GPSSpeedRef', 'type': TYPES.Ascii},
13: {'name': 'GPSSpeed', 'type': TYPES.Rational},
14: {'name': 'GPSTrackRef', 'type': TYPES.Ascii},
15: {'name': 'GPSTrack', 'type': TYPES.Rational},
16: {'name': 'GPSImgDirectionRef', 'type': TYPES.Ascii},
17: {'name': 'GPSImgDirection', 'type': TYPES.Rational},
18: {'name': 'GPSMapDatum', 'type': TYPES.Ascii},
19: {'name': 'GPSDestLatitudeRef', 'type': TYPES.Ascii},
20: {'name': 'GPSDestLatitude', 'type': TYPES.Rational},
21: {'name': 'GPSDestLongitudeRef', 'type': TYPES.Ascii},
22: {'name': 'GPSDestLongitude', 'type': TYPES.Rational},
23: {'name': 'GPSDestBearingRef', 'type': TYPES.Ascii},
24: {'name': 'GPSDestBearing', 'type': TYPES.Rational},
25: {'name': 'GPSDestDistanceRef', 'type': TYPES.Ascii},
26: {'name': 'GPSDestDistance', 'type': TYPES.Rational},
27: {'name': 'GPSProcessingMethod', 'type': TYPES.Undefined},
28: {'name': 'GPSAreaInformation', 'type': TYPES.Undefined},
29: {'name': 'GPSDateStamp', 'type': TYPES.Ascii},
30: {'name': 'GPSDifferential', 'type': TYPES.Short},
31: {'name': 'GPSHPositioningError', 'type': TYPES.Rational}},
'Interop': {1: {'name': 'InteroperabilityIndex', 'type': TYPES.Ascii}},
"Image": {
11: {"name": "ProcessingSoftware", "type": TYPES.Ascii},
254: {"name": "NewSubfileType", "type": TYPES.Long},
255: {"name": "SubfileType", "type": TYPES.Short},
256: {"name": "ImageWidth", "type": TYPES.Long},
257: {"name": "ImageLength", "type": TYPES.Long},
258: {"name": "BitsPerSample", "type": TYPES.Short},
259: {"name": "Compression", "type": TYPES.Short},
262: {"name": "PhotometricInterpretation", "type": TYPES.Short},
263: {"name": "Threshholding", "type": TYPES.Short},
264: {"name": "CellWidth", "type": TYPES.Short},
265: {"name": "CellLength", "type": TYPES.Short},
266: {"name": "FillOrder", "type": TYPES.Short},
269: {"name": "DocumentName", "type": TYPES.Ascii},
270: {"name": "ImageDescription", "type": TYPES.Ascii},
271: {"name": "Make", "type": TYPES.Ascii},
272: {"name": "Model", "type": TYPES.Ascii},
273: {"name": "StripOffsets", "type": TYPES.Long},
274: {"name": "Orientation", "type": TYPES.Short},
277: {"name": "SamplesPerPixel", "type": TYPES.Short},
278: {"name": "RowsPerStrip", "type": TYPES.Long},
279: {"name": "StripByteCounts", "type": TYPES.Long},
282: {"name": "XResolution", "type": TYPES.Rational},
283: {"name": "YResolution", "type": TYPES.Rational},
284: {"name": "PlanarConfiguration", "type": TYPES.Short},
290: {"name": "GrayResponseUnit", "type": TYPES.Short},
291: {"name": "GrayResponseCurve", "type": TYPES.Short},
292: {"name": "T4Options", "type": TYPES.Long},
293: {"name": "T6Options", "type": TYPES.Long},
296: {"name": "ResolutionUnit", "type": TYPES.Short},
301: {"name": "TransferFunction", "type": TYPES.Short},
305: {"name": "Software", "type": TYPES.Ascii},
306: {"name": "DateTime", "type": TYPES.Ascii},
315: {"name": "Artist", "type": TYPES.Ascii},
316: {"name": "HostComputer", "type": TYPES.Ascii},
317: {"name": "Predictor", "type": TYPES.Short},
318: {"name": "WhitePoint", "type": TYPES.Rational},
319: {"name": "PrimaryChromaticities", "type": TYPES.Rational},
320: {"name": "ColorMap", "type": TYPES.Short},
321: {"name": "HalftoneHints", "type": TYPES.Short},
322: {"name": "TileWidth", "type": TYPES.Short},
323: {"name": "TileLength", "type": TYPES.Short},
324: {"name": "TileOffsets", "type": TYPES.Short},
325: {"name": "TileByteCounts", "type": TYPES.Short},
330: {"name": "SubIFDs", "type": TYPES.Long},
332: {"name": "InkSet", "type": TYPES.Short},
333: {"name": "InkNames", "type": TYPES.Ascii},
334: {"name": "NumberOfInks", "type": TYPES.Short},
336: {"name": "DotRange", "type": TYPES.Byte},
337: {"name": "TargetPrinter", "type": TYPES.Ascii},
338: {"name": "ExtraSamples", "type": TYPES.Short},
339: {"name": "SampleFormat", "type": TYPES.Short},
340: {"name": "SMinSampleValue", "type": TYPES.Short},
341: {"name": "SMaxSampleValue", "type": TYPES.Short},
342: {"name": "TransferRange", "type": TYPES.Short},
343: {"name": "ClipPath", "type": TYPES.Byte},
344: {"name": "XClipPathUnits", "type": TYPES.Long},
345: {"name": "YClipPathUnits", "type": TYPES.Long},
346: {"name": "Indexed", "type": TYPES.Short},
347: {"name": "JPEGTables", "type": TYPES.Undefined},
351: {"name": "OPIProxy", "type": TYPES.Short},
512: {"name": "JPEGProc", "type": TYPES.Long},
513: {"name": "JPEGInterchangeFormat", "type": TYPES.Long},
514: {"name": "JPEGInterchangeFormatLength", "type": TYPES.Long},
515: {"name": "JPEGRestartInterval", "type": TYPES.Short},
517: {"name": "JPEGLosslessPredictors", "type": TYPES.Short},
518: {"name": "JPEGPointTransforms", "type": TYPES.Short},
519: {"name": "JPEGQTables", "type": TYPES.Long},
520: {"name": "JPEGDCTables", "type": TYPES.Long},
521: {"name": "JPEGACTables", "type": TYPES.Long},
529: {"name": "YCbCrCoefficients", "type": TYPES.Rational},
530: {"name": "YCbCrSubSampling", "type": TYPES.Short},
531: {"name": "YCbCrPositioning", "type": TYPES.Short},
532: {"name": "ReferenceBlackWhite", "type": TYPES.Rational},
700: {"name": "XMLPacket", "type": TYPES.Byte},
18246: {"name": "Rating", "type": TYPES.Short},
18249: {"name": "RatingPercent", "type": TYPES.Short},
32781: {"name": "ImageID", "type": TYPES.Ascii},
33421: {"name": "CFARepeatPatternDim", "type": TYPES.Short},
33422: {"name": "CFAPattern", "type": TYPES.Byte},
33423: {"name": "BatteryLevel", "type": TYPES.Rational},
33432: {"name": "Copyright", "type": TYPES.Ascii},
33434: {"name": "ExposureTime", "type": TYPES.Rational},
34377: {"name": "ImageResources", "type": TYPES.Byte},
34665: {"name": "ExifTag", "type": TYPES.Long},
34675: {"name": "InterColorProfile", "type": TYPES.Undefined},
34853: {"name": "GPSTag", "type": TYPES.Long},
34857: {"name": "Interlace", "type": TYPES.Short},
34858: {"name": "TimeZoneOffset", "type": TYPES.Long},
34859: {"name": "SelfTimerMode", "type": TYPES.Short},
37387: {"name": "FlashEnergy", "type": TYPES.Rational},
37388: {"name": "SpatialFrequencyResponse", "type": TYPES.Undefined},
37389: {"name": "Noise", "type": TYPES.Undefined},
37390: {"name": "FocalPlaneXResolution", "type": TYPES.Rational},
37391: {"name": "FocalPlaneYResolution", "type": TYPES.Rational},
37392: {"name": "FocalPlaneResolutionUnit", "type": TYPES.Short},
37393: {"name": "ImageNumber", "type": TYPES.Long},
37394: {"name": "SecurityClassification", "type": TYPES.Ascii},
37395: {"name": "ImageHistory", "type": TYPES.Ascii},
37397: {"name": "ExposureIndex", "type": TYPES.Rational},
37398: {"name": "TIFFEPStandardID", "type": TYPES.Byte},
37399: {"name": "SensingMethod", "type": TYPES.Short},
40091: {"name": "XPTitle", "type": TYPES.Byte},
40092: {"name": "XPComment", "type": TYPES.Byte},
40093: {"name": "XPAuthor", "type": TYPES.Byte},
40094: {"name": "XPKeywords", "type": TYPES.Byte},
40095: {"name": "XPSubject", "type": TYPES.Byte},
50341: {"name": "PrintImageMatching", "type": TYPES.Undefined},
50706: {"name": "DNGVersion", "type": TYPES.Byte},
50707: {"name": "DNGBackwardVersion", "type": TYPES.Byte},
50708: {"name": "UniqueCameraModel", "type": TYPES.Ascii},
50709: {"name": "LocalizedCameraModel", "type": TYPES.Byte},
50710: {"name": "CFAPlaneColor", "type": TYPES.Byte},
50711: {"name": "CFALayout", "type": TYPES.Short},
50712: {"name": "LinearizationTable", "type": TYPES.Short},
50713: {"name": "BlackLevelRepeatDim", "type": TYPES.Short},
50714: {"name": "BlackLevel", "type": TYPES.Rational},
50715: {"name": "BlackLevelDeltaH", "type": TYPES.SRational},
50716: {"name": "BlackLevelDeltaV", "type": TYPES.SRational},
50717: {"name": "WhiteLevel", "type": TYPES.Short},
50718: {"name": "DefaultScale", "type": TYPES.Rational},
50719: {"name": "DefaultCropOrigin", "type": TYPES.Short},
50720: {"name": "DefaultCropSize", "type": TYPES.Short},
50721: {"name": "ColorMatrix1", "type": TYPES.SRational},
50722: {"name": "ColorMatrix2", "type": TYPES.SRational},
50723: {"name": "CameraCalibration1", "type": TYPES.SRational},
50724: {"name": "CameraCalibration2", "type": TYPES.SRational},
50725: {"name": "ReductionMatrix1", "type": TYPES.SRational},
50726: {"name": "ReductionMatrix2", "type": TYPES.SRational},
50727: {"name": "AnalogBalance", "type": TYPES.Rational},
50728: {"name": "AsShotNeutral", "type": TYPES.Short},
50729: {"name": "AsShotWhiteXY", "type": TYPES.Rational},
50730: {"name": "BaselineExposure", "type": TYPES.SRational},
50731: {"name": "BaselineNoise", "type": TYPES.Rational},
50732: {"name": "BaselineSharpness", "type": TYPES.Rational},
50733: {"name": "BayerGreenSplit", "type": TYPES.Long},
50734: {"name": "LinearResponseLimit", "type": TYPES.Rational},
50735: {"name": "CameraSerialNumber", "type": TYPES.Ascii},
50736: {"name": "LensInfo", "type": TYPES.Rational},
50737: {"name": "ChromaBlurRadius", "type": TYPES.Rational},
50738: {"name": "AntiAliasStrength", "type": TYPES.Rational},
50739: {"name": "ShadowScale", "type": TYPES.SRational},
50740: {"name": "DNGPrivateData", "type": TYPES.Byte},
50741: {"name": "MakerNoteSafety", "type": TYPES.Short},
50778: {"name": "CalibrationIlluminant1", "type": TYPES.Short},
50779: {"name": "CalibrationIlluminant2", "type": TYPES.Short},
50780: {"name": "BestQualityScale", "type": TYPES.Rational},
50781: {"name": "RawDataUniqueID", "type": TYPES.Byte},
50827: {"name": "OriginalRawFileName", "type": TYPES.Byte},
50828: {"name": "OriginalRawFileData", "type": TYPES.Undefined},
50829: {"name": "ActiveArea", "type": TYPES.Short},
50830: {"name": "MaskedAreas", "type": TYPES.Short},
50831: {"name": "AsShotICCProfile", "type": TYPES.Undefined},
50832: {"name": "AsShotPreProfileMatrix", "type": TYPES.SRational},
50833: {"name": "CurrentICCProfile", "type": TYPES.Undefined},
50834: {"name": "CurrentPreProfileMatrix", "type": TYPES.SRational},
50879: {"name": "ColorimetricReference", "type": TYPES.Short},
50931: {"name": "CameraCalibrationSignature", "type": TYPES.Byte},
50932: {"name": "ProfileCalibrationSignature", "type": TYPES.Byte},
50934: {"name": "AsShotProfileName", "type": TYPES.Byte},
50935: {"name": "NoiseReductionApplied", "type": TYPES.Rational},
50936: {"name": "ProfileName", "type": TYPES.Byte},
50937: {"name": "ProfileHueSatMapDims", "type": TYPES.Long},
50938: {"name": "ProfileHueSatMapData1", "type": TYPES.Float},
50939: {"name": "ProfileHueSatMapData2", "type": TYPES.Float},
50940: {"name": "ProfileToneCurve", "type": TYPES.Float},
50941: {"name": "ProfileEmbedPolicy", "type": TYPES.Long},
50942: {"name": "ProfileCopyright", "type": TYPES.Byte},
50964: {"name": "ForwardMatrix1", "type": TYPES.SRational},
50965: {"name": "ForwardMatrix2", "type": TYPES.SRational},
50966: {"name": "PreviewApplicationName", "type": TYPES.Byte},
50967: {"name": "PreviewApplicationVersion", "type": TYPES.Byte},
50968: {"name": "PreviewSettingsName", "type": TYPES.Byte},
50969: {"name": "PreviewSettingsDigest", "type": TYPES.Byte},
50970: {"name": "PreviewColorSpace", "type": TYPES.Long},
50971: {"name": "PreviewDateTime", "type": TYPES.Ascii},
50972: {"name": "RawImageDigest", "type": TYPES.Undefined},
50973: {"name": "OriginalRawFileDigest", "type": TYPES.Undefined},
50974: {"name": "SubTileBlockSize", "type": TYPES.Long},
50975: {"name": "RowInterleaveFactor", "type": TYPES.Long},
50981: {"name": "ProfileLookTableDims", "type": TYPES.Long},
50982: {"name": "ProfileLookTableData", "type": TYPES.Float},
51008: {"name": "OpcodeList1", "type": TYPES.Undefined},
51009: {"name": "OpcodeList2", "type": TYPES.Undefined},
51022: {"name": "OpcodeList3", "type": TYPES.Undefined},
60606: {"name": "ZZZTestSlong1", "type": TYPES.SLong},
60607: {"name": "ZZZTestSlong2", "type": TYPES.SLong},
60608: {"name": "ZZZTestSByte", "type": TYPES.SByte},
60609: {"name": "ZZZTestSShort", "type": TYPES.SShort},
60610: {"name": "ZZZTestDFloat", "type": TYPES.DFloat},
},
"Exif": {
33434: {"name": "ExposureTime", "type": TYPES.Rational},
33437: {"name": "FNumber", "type": TYPES.Rational},
34850: {"name": "ExposureProgram", "type": TYPES.Short},
34852: {"name": "SpectralSensitivity", "type": TYPES.Ascii},
34855: {"name": "ISOSpeedRatings", "type": TYPES.Short},
34856: {"name": "OECF", "type": TYPES.Undefined},
34864: {"name": "SensitivityType", "type": TYPES.Short},
34865: {"name": "StandardOutputSensitivity", "type": TYPES.Long},
34866: {"name": "RecommendedExposureIndex", "type": TYPES.Long},
34867: {"name": "ISOSpeed", "type": TYPES.Long},
34868: {"name": "ISOSpeedLatitudeyyy", "type": TYPES.Long},
34869: {"name": "ISOSpeedLatitudezzz", "type": TYPES.Long},
36864: {"name": "ExifVersion", "type": TYPES.Undefined},
36867: {"name": "DateTimeOriginal", "type": TYPES.Ascii},
36868: {"name": "DateTimeDigitized", "type": TYPES.Ascii},
36880: {"name": "OffsetTime", "type": TYPES.Ascii},
36881: {"name": "OffsetTimeOriginal", "type": TYPES.Ascii},
36882: {"name": "OffsetTimeDigitized", "type": TYPES.Ascii},
37121: {"name": "ComponentsConfiguration", "type": TYPES.Undefined},
37122: {"name": "CompressedBitsPerPixel", "type": TYPES.Rational},
37377: {"name": "ShutterSpeedValue", "type": TYPES.SRational},
37378: {"name": "ApertureValue", "type": TYPES.Rational},
37379: {"name": "BrightnessValue", "type": TYPES.SRational},
37380: {"name": "ExposureBiasValue", "type": TYPES.SRational},
37381: {"name": "MaxApertureValue", "type": TYPES.Rational},
37382: {"name": "SubjectDistance", "type": TYPES.Rational},
37383: {"name": "MeteringMode", "type": TYPES.Short},
37384: {"name": "LightSource", "type": TYPES.Short},
37385: {"name": "Flash", "type": TYPES.Short},
37386: {"name": "FocalLength", "type": TYPES.Rational},
37396: {"name": "SubjectArea", "type": TYPES.Short},
37500: {"name": "MakerNote", "type": TYPES.Undefined},
37510: {"name": "UserComment", "type": TYPES.Undefined},
37520: {"name": "SubSecTime", "type": TYPES.Ascii},
37521: {"name": "SubSecTimeOriginal", "type": TYPES.Ascii},
37522: {"name": "SubSecTimeDigitized", "type": TYPES.Ascii},
37888: {"name": "Temperature", "type": TYPES.SRational},
37889: {"name": "Humidity", "type": TYPES.Rational},
37890: {"name": "Pressure", "type": TYPES.Rational},
37891: {"name": "WaterDepth", "type": TYPES.SRational},
37892: {"name": "Acceleration", "type": TYPES.Rational},
37893: {"name": "CameraElevationAngle", "type": TYPES.SRational},
40960: {"name": "FlashpixVersion", "type": TYPES.Undefined},
40961: {"name": "ColorSpace", "type": TYPES.Short},
40962: {"name": "PixelXDimension", "type": TYPES.Long},
40963: {"name": "PixelYDimension", "type": TYPES.Long},
40964: {"name": "RelatedSoundFile", "type": TYPES.Ascii},
40965: {"name": "InteroperabilityTag", "type": TYPES.Long},
41483: {"name": "FlashEnergy", "type": TYPES.Rational},
41484: {"name": "SpatialFrequencyResponse", "type": TYPES.Undefined},
41486: {"name": "FocalPlaneXResolution", "type": TYPES.Rational},
41487: {"name": "FocalPlaneYResolution", "type": TYPES.Rational},
41488: {"name": "FocalPlaneResolutionUnit", "type": TYPES.Short},
41492: {"name": "SubjectLocation", "type": TYPES.Short},
41493: {"name": "ExposureIndex", "type": TYPES.Rational},
41495: {"name": "SensingMethod", "type": TYPES.Short},
41728: {"name": "FileSource", "type": TYPES.Undefined},
41729: {"name": "SceneType", "type": TYPES.Undefined},
41730: {"name": "CFAPattern", "type": TYPES.Undefined},
41985: {"name": "CustomRendered", "type": TYPES.Short},
41986: {"name": "ExposureMode", "type": TYPES.Short},
41987: {"name": "WhiteBalance", "type": TYPES.Short},
41988: {"name": "DigitalZoomRatio", "type": TYPES.Rational},
41989: {"name": "FocalLengthIn35mmFilm", "type": TYPES.Short},
41990: {"name": "SceneCaptureType", "type": TYPES.Short},
41991: {"name": "GainControl", "type": TYPES.Short},
41992: {"name": "Contrast", "type": TYPES.Short},
41993: {"name": "Saturation", "type": TYPES.Short},
41994: {"name": "Sharpness", "type": TYPES.Short},
41995: {"name": "DeviceSettingDescription", "type": TYPES.Undefined},
41996: {"name": "SubjectDistanceRange", "type": TYPES.Short},
42016: {"name": "ImageUniqueID", "type": TYPES.Ascii},
42032: {"name": "CameraOwnerName", "type": TYPES.Ascii},
42033: {"name": "BodySerialNumber", "type": TYPES.Ascii},
42034: {"name": "LensSpecification", "type": TYPES.Rational},
42035: {"name": "LensMake", "type": TYPES.Ascii},
42036: {"name": "LensModel", "type": TYPES.Ascii},
42037: {"name": "LensSerialNumber", "type": TYPES.Ascii},
42240: {"name": "Gamma", "type": TYPES.Rational},
},
"GPS": {
0: {"name": "GPSVersionID", "type": TYPES.Byte},
1: {"name": "GPSLatitudeRef", "type": TYPES.Ascii},
2: {"name": "GPSLatitude", "type": TYPES.Rational},
3: {"name": "GPSLongitudeRef", "type": TYPES.Ascii},
4: {"name": "GPSLongitude", "type": TYPES.Rational},
5: {"name": "GPSAltitudeRef", "type": TYPES.Byte},
6: {"name": "GPSAltitude", "type": TYPES.Rational},
7: {"name": "GPSTimeStamp", "type": TYPES.Rational},
8: {"name": "GPSSatellites", "type": TYPES.Ascii},
9: {"name": "GPSStatus", "type": TYPES.Ascii},
10: {"name": "GPSMeasureMode", "type": TYPES.Ascii},
11: {"name": "GPSDOP", "type": TYPES.Rational},
12: {"name": "GPSSpeedRef", "type": TYPES.Ascii},
13: {"name": "GPSSpeed", "type": TYPES.Rational},
14: {"name": "GPSTrackRef", "type": TYPES.Ascii},
15: {"name": "GPSTrack", "type": TYPES.Rational},
16: {"name": "GPSImgDirectionRef", "type": TYPES.Ascii},
17: {"name": "GPSImgDirection", "type": TYPES.Rational},
18: {"name": "GPSMapDatum", "type": TYPES.Ascii},
19: {"name": "GPSDestLatitudeRef", "type": TYPES.Ascii},
20: {"name": "GPSDestLatitude", "type": TYPES.Rational},
21: {"name": "GPSDestLongitudeRef", "type": TYPES.Ascii},
22: {"name": "GPSDestLongitude", "type": TYPES.Rational},
23: {"name": "GPSDestBearingRef", "type": TYPES.Ascii},
24: {"name": "GPSDestBearing", "type": TYPES.Rational},
25: {"name": "GPSDestDistanceRef", "type": TYPES.Ascii},
26: {"name": "GPSDestDistance", "type": TYPES.Rational},
27: {"name": "GPSProcessingMethod", "type": TYPES.Undefined},
28: {"name": "GPSAreaInformation", "type": TYPES.Undefined},
29: {"name": "GPSDateStamp", "type": TYPES.Ascii},
30: {"name": "GPSDifferential", "type": TYPES.Short},
31: {"name": "GPSHPositioningError", "type": TYPES.Rational},
},
"Interop": {1: {"name": "InteroperabilityIndex", "type": TYPES.Ascii}},
}
TAGS["0th"] = TAGS["Image"]
TAGS["1st"] = TAGS["Image"]
class ImageIFD:
"""Exif tag number reference - 0th IFD"""
ProcessingSoftware = 11
NewSubfileType = 254
SubfileType = 255
@ -516,6 +524,7 @@ class ImageIFD:
class ExifIFD:
"""Exif tag number reference - Exif IFD"""
ExposureTime = 33434
FNumber = 33437
ExposureProgram = 34850
@ -599,6 +608,7 @@ class ExifIFD:
class GPSIFD:
"""Exif tag number reference - GPS IFD"""
GPSVersionID = 0
GPSLatitudeRef = 1
GPSLatitude = 2
@ -635,4 +645,5 @@ class GPSIFD:
class InteropIFD:
"""Exif tag number reference - Interoperability IFD"""
InteroperabilityIndex = 1

View file

@ -6,6 +6,7 @@ from ._common import *
from ._exceptions import InvalidImageDataError
from . import _webp
def insert(exif, image, new_file=None):
"""
py:function:: piexif.insert(exif_bytes, filename)
@ -20,7 +21,7 @@ def insert(exif, image, new_file=None):
output_file = False
# Prevents "UnicodeWarning: Unicode equal comparison failed" warnings on Python 2
maybe_image = sys.version_info >= (3,0,0) or isinstance(image, str)
maybe_image = sys.version_info >= (3, 0, 0) or isinstance(image, str)
if maybe_image and image[0:2] == b"\xff\xd8":
image_data = image
@ -29,7 +30,7 @@ def insert(exif, image, new_file=None):
image_data = image
file_type = "webp"
else:
with open(image, 'rb') as f:
with open(image, "rb") as f:
image_data = f.read()
if image_data[0:2] == b"\xff\xd8":
file_type = "jpeg"
@ -57,4 +58,4 @@ def insert(exif, image, new_file=None):
with open(image, "wb+") as f:
f.write(new_data)
else:
raise ValueError("Give a 3rd argument to 'insert' to output file")
raise ValueError("Give a 3rd argument to 'insert' to output file")

View file

@ -19,12 +19,14 @@ def load(input_data, key_is_name=False):
:return: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
:rtype: dict
"""
exif_dict = {"0th":{},
"Exif":{},
"GPS":{},
"Interop":{},
"1st":{},
"thumbnail":None}
exif_dict = {
"0th": {},
"Exif": {},
"GPS": {},
"Interop": {},
"1st": {},
"thumbnail": None,
}
exifReader = _ExifReader(input_data)
if exifReader.tiftag is None:
return exif_dict
@ -34,8 +36,7 @@ def load(input_data, key_is_name=False):
else:
exifReader.endian_mark = ">"
pointer = struct.unpack(exifReader.endian_mark + "L",
exifReader.tiftag[4:8])[0]
pointer = struct.unpack(exifReader.endian_mark + "L", exifReader.tiftag[4:8])[0]
exif_dict["0th"] = exifReader.get_ifd_dict(pointer, "0th")
first_ifd_pointer = exif_dict["0th"].pop("first_ifd_pointer")
if ImageIFD.ExifTag in exif_dict["0th"]:
@ -48,14 +49,19 @@ def load(input_data, key_is_name=False):
pointer = exif_dict["Exif"][ExifIFD.InteroperabilityTag]
exif_dict["Interop"] = exifReader.get_ifd_dict(pointer, "Interop")
if first_ifd_pointer != b"\x00\x00\x00\x00":
pointer = struct.unpack(exifReader.endian_mark + "L",
first_ifd_pointer)[0]
pointer = struct.unpack(exifReader.endian_mark + "L", first_ifd_pointer)[0]
exif_dict["1st"] = exifReader.get_ifd_dict(pointer, "1st")
if (ImageIFD.JPEGInterchangeFormat in exif_dict["1st"] and
ImageIFD.JPEGInterchangeFormatLength in exif_dict["1st"]):
end = (exif_dict["1st"][ImageIFD.JPEGInterchangeFormat] +
exif_dict["1st"][ImageIFD.JPEGInterchangeFormatLength])
thumb = exifReader.tiftag[exif_dict["1st"][ImageIFD.JPEGInterchangeFormat]:end]
if (
ImageIFD.JPEGInterchangeFormat in exif_dict["1st"]
and ImageIFD.JPEGInterchangeFormatLength in exif_dict["1st"]
):
end = (
exif_dict["1st"][ImageIFD.JPEGInterchangeFormat]
+ exif_dict["1st"][ImageIFD.JPEGInterchangeFormatLength]
)
thumb = exifReader.tiftag[
exif_dict["1st"][ImageIFD.JPEGInterchangeFormat] : end
]
exif_dict["thumbnail"] = thumb
if key_is_name:
@ -66,7 +72,7 @@ def load(input_data, key_is_name=False):
class _ExifReader(object):
def __init__(self, data):
# Prevents "UnicodeWarning: Unicode equal comparison failed" warnings on Python 2
maybe_image = sys.version_info >= (3,0,0) or isinstance(data, str)
maybe_image = sys.version_info >= (3, 0, 0) or isinstance(data, str)
if maybe_image and data[0:2] == b"\xff\xd8": # JPEG
segments = split_into_segments(data)
@ -82,7 +88,7 @@ class _ExifReader(object):
elif maybe_image and data[0:4] == b"Exif": # Exif
self.tiftag = data[6:]
else:
with open(data, 'rb') as f:
with open(data, "rb") as f:
magic_number = f.read(2)
if magic_number == b"\xff\xd8": # JPEG
app1 = read_exif_from_file(data)
@ -91,13 +97,13 @@ class _ExifReader(object):
else:
self.tiftag = None
elif magic_number in (b"\x49\x49", b"\x4d\x4d"): # TIFF
with open(data, 'rb') as f:
with open(data, "rb") as f:
self.tiftag = f.read()
else:
with open(data, 'rb') as f:
with open(data, "rb") as f:
header = f.read(12)
if header[0:4] == b"RIFF"and header[8:12] == b"WEBP":
with open(data, 'rb') as f:
if header[0:4] == b"RIFF" and header[8:12] == b"WEBP":
with open(data, "rb") as f:
file_data = f.read()
self.tiftag = _webp.get_exif(file_data)
else:
@ -105,8 +111,9 @@ class _ExifReader(object):
def get_ifd_dict(self, pointer, ifd_name, read_unknown=False):
ifd_dict = {}
tag_count = struct.unpack(self.endian_mark + "H",
self.tiftag[pointer: pointer+2])[0]
tag_count = struct.unpack(
self.endian_mark + "H", self.tiftag[pointer : pointer + 2]
)[0]
offset = pointer + 2
if ifd_name in ["0th", "1st"]:
t = "Image"
@ -115,26 +122,28 @@ class _ExifReader(object):
p_and_value = []
for x in range(tag_count):
pointer = offset + 12 * x
tag = struct.unpack(self.endian_mark + "H",
self.tiftag[pointer: pointer+2])[0]
value_type = struct.unpack(self.endian_mark + "H",
self.tiftag[pointer + 2: pointer + 4])[0]
value_num = struct.unpack(self.endian_mark + "L",
self.tiftag[pointer + 4: pointer + 8]
)[0]
value = self.tiftag[pointer+8: pointer+12]
tag = struct.unpack(
self.endian_mark + "H", self.tiftag[pointer : pointer + 2]
)[0]
value_type = struct.unpack(
self.endian_mark + "H", self.tiftag[pointer + 2 : pointer + 4]
)[0]
value_num = struct.unpack(
self.endian_mark + "L", self.tiftag[pointer + 4 : pointer + 8]
)[0]
value = self.tiftag[pointer + 8 : pointer + 12]
p_and_value.append((pointer, value_type, value_num, value))
v_set = (value_type, value_num, value, tag)
if tag in TAGS[t]:
ifd_dict[tag] = self.convert_value(v_set)
elif read_unknown:
ifd_dict[tag] = (v_set[0], v_set[1], v_set[2], self.tiftag)
#else:
# else:
# pass
if ifd_name == "0th":
pointer = offset + 12 * tag_count
ifd_dict["first_ifd_pointer"] = self.tiftag[pointer:pointer + 4]
ifd_dict["first_ifd_pointer"] = self.tiftag[pointer : pointer + 4]
return ifd_dict
def convert_value(self, val):
@ -143,114 +152,148 @@ class _ExifReader(object):
length = val[1]
value = val[2]
if t == TYPES.Byte: # BYTE
if t == TYPES.Byte: # BYTE
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack("B" * length,
self.tiftag[pointer: pointer + length])
data = struct.unpack(
"B" * length, self.tiftag[pointer : pointer + length]
)
else:
data = struct.unpack("B" * length, value[0:length])
elif t == TYPES.Ascii: # ASCII
elif t == TYPES.Ascii: # ASCII
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = self.tiftag[pointer: pointer+length - 1]
data = self.tiftag[pointer : pointer + length - 1]
else:
data = value[0: length - 1]
elif t == TYPES.Short: # SHORT
data = value[0 : length - 1]
elif t == TYPES.Short: # SHORT
if length > 2:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "H" * length,
self.tiftag[pointer: pointer+length*2])
data = struct.unpack(
self.endian_mark + "H" * length,
self.tiftag[pointer : pointer + length * 2],
)
else:
data = struct.unpack(self.endian_mark + "H" * length,
value[0:length * 2])
elif t == TYPES.Long: # LONG
data = struct.unpack(
self.endian_mark + "H" * length, value[0 : length * 2]
)
elif t == TYPES.Long: # LONG
if length > 1:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "L" * length,
self.tiftag[pointer: pointer+length*4])
data = struct.unpack(
self.endian_mark + "L" * length,
self.tiftag[pointer : pointer + length * 4],
)
else:
data = struct.unpack(self.endian_mark + "L" * length,
value)
elif t == TYPES.Rational: # RATIONAL
data = struct.unpack(self.endian_mark + "L" * length, value)
elif t == TYPES.Rational: # RATIONAL
pointer = struct.unpack(self.endian_mark + "L", value)[0]
if length > 1:
data = tuple(
(struct.unpack(self.endian_mark + "L",
self.tiftag[pointer + x * 8:
pointer + 4 + x * 8])[0],
struct.unpack(self.endian_mark + "L",
self.tiftag[pointer + 4 + x * 8:
pointer + 8 + x * 8])[0])
(
struct.unpack(
self.endian_mark + "L",
self.tiftag[pointer + x * 8 : pointer + 4 + x * 8],
)[0],
struct.unpack(
self.endian_mark + "L",
self.tiftag[pointer + 4 + x * 8 : pointer + 8 + x * 8],
)[0],
)
for x in range(length)
)
else:
data = (struct.unpack(self.endian_mark + "L",
self.tiftag[pointer: pointer + 4])[0],
struct.unpack(self.endian_mark + "L",
self.tiftag[pointer + 4: pointer + 8]
)[0])
elif t == TYPES.SByte: # SIGNED BYTES
data = (
struct.unpack(
self.endian_mark + "L", self.tiftag[pointer : pointer + 4]
)[0],
struct.unpack(
self.endian_mark + "L", self.tiftag[pointer + 4 : pointer + 8]
)[0],
)
elif t == TYPES.SByte: # SIGNED BYTES
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack("b" * length,
self.tiftag[pointer: pointer + length])
data = struct.unpack(
"b" * length, self.tiftag[pointer : pointer + length]
)
else:
data = struct.unpack("b" * length, value[0:length])
elif t == TYPES.Undefined: # UNDEFINED BYTES
elif t == TYPES.Undefined: # UNDEFINED BYTES
if length > 4:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = self.tiftag[pointer: pointer+length]
data = self.tiftag[pointer : pointer + length]
else:
data = value[0:length]
elif t == TYPES.SShort: # SIGNED SHORT
elif t == TYPES.SShort: # SIGNED SHORT
if length > 2:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "h" * length,
self.tiftag[pointer: pointer+length*2])
data = struct.unpack(
self.endian_mark + "h" * length,
self.tiftag[pointer : pointer + length * 2],
)
else:
data = struct.unpack(self.endian_mark + "h" * length,
value[0:length * 2])
elif t == TYPES.SLong: # SLONG
data = struct.unpack(
self.endian_mark + "h" * length, value[0 : length * 2]
)
elif t == TYPES.SLong: # SLONG
if length > 1:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "l" * length,
self.tiftag[pointer: pointer+length*4])
data = struct.unpack(
self.endian_mark + "l" * length,
self.tiftag[pointer : pointer + length * 4],
)
else:
data = struct.unpack(self.endian_mark + "l" * length,
value)
elif t == TYPES.SRational: # SRATIONAL
data = struct.unpack(self.endian_mark + "l" * length, value)
elif t == TYPES.SRational: # SRATIONAL
pointer = struct.unpack(self.endian_mark + "L", value)[0]
if length > 1:
data = tuple(
(struct.unpack(self.endian_mark + "l",
self.tiftag[pointer + x * 8: pointer + 4 + x * 8])[0],
struct.unpack(self.endian_mark + "l",
self.tiftag[pointer + 4 + x * 8: pointer + 8 + x * 8])[0])
for x in range(length)
(
struct.unpack(
self.endian_mark + "l",
self.tiftag[pointer + x * 8 : pointer + 4 + x * 8],
)[0],
struct.unpack(
self.endian_mark + "l",
self.tiftag[pointer + 4 + x * 8 : pointer + 8 + x * 8],
)[0],
)
for x in range(length)
)
else:
data = (struct.unpack(self.endian_mark + "l",
self.tiftag[pointer: pointer + 4])[0],
struct.unpack(self.endian_mark + "l",
self.tiftag[pointer + 4: pointer + 8]
)[0])
elif t == TYPES.Float: # FLOAT
data = (
struct.unpack(
self.endian_mark + "l", self.tiftag[pointer : pointer + 4]
)[0],
struct.unpack(
self.endian_mark + "l", self.tiftag[pointer + 4 : pointer + 8]
)[0],
)
elif t == TYPES.Float: # FLOAT
if length > 1:
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "f" * length,
self.tiftag[pointer: pointer+length*4])
data = struct.unpack(
self.endian_mark + "f" * length,
self.tiftag[pointer : pointer + length * 4],
)
else:
data = struct.unpack(self.endian_mark + "f" * length,
value)
elif t == TYPES.DFloat: # DOUBLE
data = struct.unpack(self.endian_mark + "f" * length, value)
elif t == TYPES.DFloat: # DOUBLE
pointer = struct.unpack(self.endian_mark + "L", value)[0]
data = struct.unpack(self.endian_mark + "d" * length,
self.tiftag[pointer: pointer+length*8])
data = struct.unpack(
self.endian_mark + "d" * length,
self.tiftag[pointer : pointer + length * 8],
)
else:
raise ValueError("Exif might be wrong. Got incorrect value " +
"type to decode.\n" +
"tag: " + str(val[3]) + "\ntype: " + str(t))
raise ValueError(
"Exif might be wrong. Got incorrect value "
+ "type to decode.\n"
+ "tag: "
+ str(val[3])
+ "\ntype: "
+ str(t)
)
if isinstance(data, tuple) and (len(data) == 1):
return data[0]
@ -260,11 +303,20 @@ class _ExifReader(object):
def _get_key_name_dict(exif_dict):
new_dict = {
"0th":{TAGS["Image"][n]["name"]:value for n, value in exif_dict["0th"].items()},
"Exif":{TAGS["Exif"][n]["name"]:value for n, value in exif_dict["Exif"].items()},
"1st":{TAGS["Image"][n]["name"]:value for n, value in exif_dict["1st"].items()},
"GPS":{TAGS["GPS"][n]["name"]:value for n, value in exif_dict["GPS"].items()},
"Interop":{TAGS["Interop"][n]["name"]:value for n, value in exif_dict["Interop"].items()},
"thumbnail":exif_dict["thumbnail"],
"0th": {
TAGS["Image"][n]["name"]: value for n, value in exif_dict["0th"].items()
},
"Exif": {
TAGS["Exif"][n]["name"]: value for n, value in exif_dict["Exif"].items()
},
"1st": {
TAGS["Image"][n]["name"]: value for n, value in exif_dict["1st"].items()
},
"GPS": {TAGS["GPS"][n]["name"]: value for n, value in exif_dict["GPS"].items()},
"Interop": {
TAGS["Interop"][n]["name"]: value
for n, value in exif_dict["Interop"].items()
},
"thumbnail": exif_dict["thumbnail"],
}
return new_dict
return new_dict

View file

@ -3,6 +3,7 @@ import io
from ._common import *
from . import _webp
def remove(src, new_file=None):
"""
py:function:: piexif.remove(filename)
@ -19,7 +20,7 @@ def remove(src, new_file=None):
src_data = src
file_type = "webp"
else:
with open(src, 'rb') as f:
with open(src, "rb") as f:
src_data = f.read()
output_is_file = True
if src_data[0:2] == b"\xff\xd8":
@ -53,4 +54,4 @@ def remove(src, new_file=None):
with open(src, "wb+") as f:
f.write(new_data)
else:
raise ValueError("Give a second argument to 'remove' to output file")
raise ValueError("Give a second argument to 'remove' to output file")

View file

@ -15,7 +15,7 @@ def transplant(exif_src, image, new_file=None):
if exif_src[0:2] == b"\xff\xd8":
src_data = exif_src
else:
with open(exif_src, 'rb') as f:
with open(exif_src, "rb") as f:
src_data = f.read()
segments = split_into_segments(src_data)
exif = get_exif_seg(segments)
@ -26,7 +26,7 @@ def transplant(exif_src, image, new_file=None):
if image[0:2] == b"\xff\xd8":
image_data = image
else:
with open(image, 'rb') as f:
with open(image, "rb") as f:
image_data = f.read()
output_file = True
segments = split_into_segments(image_data)
@ -42,4 +42,4 @@ def transplant(exif_src, image, new_file=None):
with open(image, "wb+") as f:
f.write(new_data)
else:
raise ValueError("Give a 3rd argument to 'transplant' to output file")
raise ValueError("Give a 3rd argument to 'transplant' to output file")

View file

@ -1,4 +1,3 @@
import struct
@ -18,26 +17,33 @@ def split(data):
chunks = []
while pointer + CHUNK_FOURCC_LENGTH + LENGTH_BYTES_LENGTH < file_size:
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
fourcc = data[pointer : pointer + CHUNK_FOURCC_LENGTH]
pointer += CHUNK_FOURCC_LENGTH
chunk_length_bytes = data[pointer:pointer + LENGTH_BYTES_LENGTH]
chunk_length_bytes = data[pointer : pointer + LENGTH_BYTES_LENGTH]
chunk_length = struct.unpack("<L", chunk_length_bytes)[0]
pointer += LENGTH_BYTES_LENGTH
chunk_data = data[pointer:pointer + chunk_length]
chunks.append({"fourcc":fourcc, "length_bytes":chunk_length_bytes, "data":chunk_data})
chunk_data = data[pointer : pointer + chunk_length]
chunks.append(
{"fourcc": fourcc, "length_bytes": chunk_length_bytes, "data": chunk_data}
)
padding = 1 if chunk_length % 2 else 0
pointer += chunk_length + padding
return chunks
def merge_chunks(chunks):
merged = b"".join([chunk["fourcc"]
+ chunk["length_bytes"]
+ chunk["data"]
+ (len(chunk["data"]) % 2) * b"\x00"
for chunk in chunks])
merged = b"".join(
[
chunk["fourcc"]
+ chunk["length_bytes"]
+ chunk["data"]
+ (len(chunk["data"]) % 2) * b"\x00"
for chunk in chunks
]
)
return merged
@ -50,6 +56,7 @@ def _get_size_from_vp8x(chunk):
height = height_minus_one + 1
return (width, height)
def _get_size_from_vp8(chunk):
BEGIN_CODE = b"\x9d\x01\x2a"
begin_index = chunk["data"].find(BEGIN_CODE)
@ -59,17 +66,21 @@ def _get_size_from_vp8(chunk):
BEGIN_CODE_LENGTH = len(BEGIN_CODE)
LENGTH_BYTES_LENGTH = 2
length_start = begin_index + BEGIN_CODE_LENGTH
width_bytes = chunk["data"][length_start:length_start + LENGTH_BYTES_LENGTH]
width_bytes = chunk["data"][length_start : length_start + LENGTH_BYTES_LENGTH]
width = struct.unpack("<H", width_bytes)[0]
height_bytes = chunk["data"][length_start + LENGTH_BYTES_LENGTH:length_start + 2 * LENGTH_BYTES_LENGTH]
height_bytes = chunk["data"][
length_start + LENGTH_BYTES_LENGTH : length_start + 2 * LENGTH_BYTES_LENGTH
]
height = struct.unpack("<H", height_bytes)[0]
return (width, height)
def _vp8L_contains_alpha(chunk_data):
flag = ord(chunk_data[4:5]) >> 5-1 & ord(b"\x01")
flag = ord(chunk_data[4:5]) >> 5 - 1 & ord(b"\x01")
contains = 1 * flag
return contains
def _get_size_from_vp8L(chunk):
b1 = chunk["data"][1:2]
b2 = chunk["data"][2:3]
@ -79,11 +90,14 @@ def _get_size_from_vp8L(chunk):
width_minus_one = (ord(b2) & ord(b"\x3F")) << 8 | ord(b1)
width = width_minus_one + 1
height_minus_one = (ord(b4) & ord(b"\x0F")) << 10 | ord(b3) << 2 | (ord(b2) & ord(b"\xC0")) >> 6
height_minus_one = (
(ord(b4) & ord(b"\x0F")) << 10 | ord(b3) << 2 | (ord(b2) & ord(b"\xC0")) >> 6
)
height = height_minus_one + 1
return (width, height)
def _get_size_from_anmf(chunk):
width_minus_one_bytes = chunk["data"][6:9] + b"\x00"
width_minus_one = struct.unpack("<L", width_minus_one_bytes)[0]
@ -92,12 +106,22 @@ def _get_size_from_anmf(chunk):
height_minus_one = struct.unpack("<L", height_minus_one_bytes)[0]
height = height_minus_one + 1
return (width, height)
def set_vp8x(chunks):
width = None
height = None
flags = [b"0", b"0", b"0", b"0", b"0", b"0", b"0", b"0"] # [0, 0, ICC, Alpha, EXIF, XMP, Anim, 0]
flags = [
b"0",
b"0",
b"0",
b"0",
b"0",
b"0",
b"0",
b"0",
] # [0, 0, ICC, Alpha, EXIF, XMP, Anim, 0]
for chunk in chunks:
if chunk["fourcc"] == b"VP8X":
@ -136,11 +160,16 @@ def set_vp8x(chunks):
data_bytes = flags_bytes + padding_bytes + width_bytes + height_bytes
vp8x_chunk = {"fourcc":header_bytes, "length_bytes":length_bytes, "data":data_bytes}
vp8x_chunk = {
"fourcc": header_bytes,
"length_bytes": length_bytes,
"data": data_bytes,
}
chunks.insert(0, vp8x_chunk)
return chunks
def get_file_header(chunks):
WEBP_HEADER_LENGTH = 4
FOURCC_LENGTH = 4
@ -158,7 +187,6 @@ def get_file_header(chunks):
return file_header
def get_exif(data):
if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
raise ValueError("Not WebP")
@ -179,15 +207,15 @@ def get_exif(data):
chunks = []
exif = b""
while pointer < file_size:
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
fourcc = data[pointer : pointer + CHUNK_FOURCC_LENGTH]
pointer += CHUNK_FOURCC_LENGTH
chunk_length_bytes = data[pointer:pointer + LENGTH_BYTES_LENGTH]
chunk_length_bytes = data[pointer : pointer + LENGTH_BYTES_LENGTH]
chunk_length = struct.unpack("<L", chunk_length_bytes)[0]
if chunk_length % 2:
chunk_length += 1
pointer += LENGTH_BYTES_LENGTH
if fourcc == b"EXIF":
return data[pointer:pointer + chunk_length]
return data[pointer : pointer + chunk_length]
pointer += chunk_length
return None # if there isn't exif, return None.
@ -195,7 +223,11 @@ def get_exif(data):
def insert_exif_into_chunks(chunks, exif_bytes):
EXIF_HEADER = b"EXIF"
exif_length_bytes = struct.pack("<L", len(exif_bytes))
exif_chunk = {"fourcc":EXIF_HEADER, "length_bytes":exif_length_bytes, "data":exif_bytes}
exif_chunk = {
"fourcc": EXIF_HEADER,
"length_bytes": exif_length_bytes,
"data": exif_bytes,
}
xmp_index = None
animation_index = None

View file

@ -2,26 +2,26 @@ class UserComment:
#
# Names of encodings that we publicly support.
#
ASCII = 'ascii'
JIS = 'jis'
UNICODE = 'unicode'
ASCII = "ascii"
JIS = "jis"
UNICODE = "unicode"
ENCODINGS = (ASCII, JIS, UNICODE)
#
# The actual encodings accepted by the standard library differ slightly from
# the above.
#
_JIS = 'shift_jis'
_UNICODE = 'utf_16_be'
_JIS = "shift_jis"
_UNICODE = "utf_16_be"
_PREFIX_SIZE = 8
#
# From Table 9: Character Codes and their Designation
#
_ASCII_PREFIX = b'\x41\x53\x43\x49\x49\x00\x00\x00'
_JIS_PREFIX = b'\x4a\x49\x53\x00\x00\x00\x00\x00'
_UNICODE_PREFIX = b'\x55\x4e\x49\x43\x4f\x44\x45\x00'
_UNDEFINED_PREFIX = b'\x00\x00\x00\x00\x00\x00\x00\x00'
_ASCII_PREFIX = b"\x41\x53\x43\x49\x49\x00\x00\x00"
_JIS_PREFIX = b"\x4a\x49\x53\x00\x00\x00\x00\x00"
_UNICODE_PREFIX = b"\x55\x4e\x49\x43\x4f\x44\x45\x00"
_UNDEFINED_PREFIX = b"\x00\x00\x00\x00\x00\x00\x00\x00"
@classmethod
def load(cls, data):
@ -35,18 +35,20 @@ class UserComment:
or the encoding is unsupported.
"""
if len(data) < cls._PREFIX_SIZE:
raise ValueError('not enough data to decode UserComment')
prefix = data[:cls._PREFIX_SIZE]
body = data[cls._PREFIX_SIZE:]
raise ValueError("not enough data to decode UserComment")
prefix = data[: cls._PREFIX_SIZE]
body = data[cls._PREFIX_SIZE :]
if prefix == cls._UNDEFINED_PREFIX:
raise ValueError('prefix is UNDEFINED, unable to decode UserComment')
raise ValueError("prefix is UNDEFINED, unable to decode UserComment")
try:
encoding = {
cls._ASCII_PREFIX: cls.ASCII, cls._JIS_PREFIX: cls._JIS, cls._UNICODE_PREFIX: cls._UNICODE,
cls._ASCII_PREFIX: cls.ASCII,
cls._JIS_PREFIX: cls._JIS,
cls._UNICODE_PREFIX: cls._UNICODE,
}[prefix]
except KeyError:
raise ValueError('unable to determine appropriate encoding')
return body.decode(encoding, errors='replace')
raise ValueError("unable to determine appropriate encoding")
return body.decode(encoding, errors="replace")
@classmethod
def dump(cls, data, encoding="ascii"):
@ -60,7 +62,15 @@ class UserComment:
:raises: ValueError if the encoding is unsupported.
"""
if encoding not in cls.ENCODINGS:
raise ValueError('encoding %r must be one of %r' % (encoding, cls.ENCODINGS))
prefix = {cls.ASCII: cls._ASCII_PREFIX, cls.JIS: cls._JIS_PREFIX, cls.UNICODE: cls._UNICODE_PREFIX}[encoding]
internal_encoding = {cls.UNICODE: cls._UNICODE, cls.JIS: cls._JIS}.get(encoding, encoding)
return prefix + data.encode(internal_encoding, errors='replace')
raise ValueError(
"encoding %r must be one of %r" % (encoding, cls.ENCODINGS)
)
prefix = {
cls.ASCII: cls._ASCII_PREFIX,
cls.JIS: cls._JIS_PREFIX,
cls.UNICODE: cls._UNICODE_PREFIX,
}[encoding]
internal_encoding = {cls.UNICODE: cls._UNICODE, cls.JIS: cls._JIS}.get(
encoding, encoding
)
return prefix + data.encode(internal_encoding, errors="replace")