checkpt
This commit is contained in:
11
lib/python3.12/site-packages/piexif/__init__.py
Normal file
11
lib/python3.12/site-packages/piexif/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from ._remove import remove
|
||||
from ._load import load
|
||||
from ._dump import dump
|
||||
from ._transplant import transplant
|
||||
from ._insert import insert
|
||||
from ._exif import *
|
||||
from ._exceptions import *
|
||||
|
||||
|
||||
|
||||
VERSION = '1.1.3'
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
94
lib/python3.12/site-packages/piexif/_common.py
Normal file
94
lib/python3.12/site-packages/piexif/_common.py
Normal file
@ -0,0 +1,94 @@
|
||||
import struct
|
||||
|
||||
from ._exceptions import InvalidImageDataError
|
||||
|
||||
|
||||
def split_into_segments(data):
|
||||
"""Slices JPEG meta data into a list from JPEG binary data.
|
||||
"""
|
||||
if data[0:2] != b"\xff\xd8":
|
||||
raise InvalidImageDataError("Given data isn't JPEG.")
|
||||
|
||||
head = 2
|
||||
segments = [b"\xff\xd8"]
|
||||
while 1:
|
||||
if data[head: head + 2] == b"\xff\xda":
|
||||
segments.append(data[head:])
|
||||
break
|
||||
else:
|
||||
length = struct.unpack(">H", data[head + 2: head + 4])[0]
|
||||
endPoint = head + length + 2
|
||||
seg = data[head: endPoint]
|
||||
segments.append(seg)
|
||||
head = endPoint
|
||||
|
||||
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.
|
||||
"""
|
||||
f = open(filename, "rb")
|
||||
data = f.read(6)
|
||||
|
||||
if data[0:2] != b"\xff\xd8":
|
||||
raise InvalidImageDataError("Given data isn't JPEG.")
|
||||
|
||||
head = data[2:6]
|
||||
HEAD_LENGTH = 4
|
||||
exif = None
|
||||
while len(head) == HEAD_LENGTH:
|
||||
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':
|
||||
head = f.read(HEAD_LENGTH)
|
||||
continue
|
||||
exif = head + segment_data
|
||||
break
|
||||
elif head[0:1] == b"\xff":
|
||||
f.read(length - 2)
|
||||
head = f.read(HEAD_LENGTH)
|
||||
else:
|
||||
break
|
||||
|
||||
f.close()
|
||||
return exif
|
||||
|
||||
def get_exif_seg(segments):
|
||||
"""Returns Exif from JPEG meta data list
|
||||
"""
|
||||
for seg in segments:
|
||||
if seg[0:2] == b"\xff\xe1" and seg[4:10] == b"Exif\x00\x00":
|
||||
return seg
|
||||
return None
|
||||
|
||||
|
||||
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 exif:
|
||||
segments[2] = exif
|
||||
segments.pop(1)
|
||||
elif exif is None:
|
||||
segments.pop(2)
|
||||
else:
|
||||
segments.pop(1)
|
||||
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":
|
||||
if exif:
|
||||
segments[1] = exif
|
||||
elif exif is None:
|
||||
segments.pop(1)
|
||||
else:
|
||||
if exif:
|
||||
segments.insert(1, exif)
|
||||
return b"".join(segments)
|
346
lib/python3.12/site-packages/piexif/_dump.py
Normal file
346
lib/python3.12/site-packages/piexif/_dump.py
Normal file
@ -0,0 +1,346 @@
|
||||
import copy
|
||||
import numbers
|
||||
import struct
|
||||
|
||||
from ._common import *
|
||||
from ._exif import *
|
||||
|
||||
|
||||
TIFF_HEADER_LENGTH = 8
|
||||
|
||||
|
||||
def dump(exif_dict_original):
|
||||
"""
|
||||
py:function:: piexif.load(data)
|
||||
|
||||
Return exif as bytes.
|
||||
|
||||
:param dict exif: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
|
||||
:return: Exif
|
||||
:rtype: bytes
|
||||
"""
|
||||
exif_dict = copy.deepcopy(exif_dict_original)
|
||||
header = b"Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08"
|
||||
exif_is = False
|
||||
gps_is = False
|
||||
interop_is = False
|
||||
first_is = False
|
||||
|
||||
if "0th" in exif_dict:
|
||||
zeroth_ifd = exif_dict["0th"]
|
||||
else:
|
||||
zeroth_ifd = {}
|
||||
|
||||
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
|
||||
interop_is = True
|
||||
interop_ifd = exif_dict["Interop"]
|
||||
elif ExifIFD. InteroperabilityTag in exif_ifd:
|
||||
exif_ifd.pop(ExifIFD.InteroperabilityTag)
|
||||
elif ImageIFD.ExifTag in zeroth_ifd:
|
||||
zeroth_ifd.pop(ImageIFD.ExifTag)
|
||||
|
||||
if ("GPS" in exif_dict) and len(exif_dict["GPS"]):
|
||||
zeroth_ifd[ImageIFD.GPSTag] = 1
|
||||
gps_is = True
|
||||
gps_ifd = exif_dict["GPS"]
|
||||
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)):
|
||||
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]))
|
||||
|
||||
if exif_is:
|
||||
exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length)
|
||||
exif_length = len(exif_set[0]) + interop_is * 12 + len(exif_set[1])
|
||||
else:
|
||||
exif_bytes = b""
|
||||
exif_length = 0
|
||||
if gps_is:
|
||||
gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length)
|
||||
gps_bytes = b"".join(gps_set)
|
||||
gps_length = len(gps_bytes)
|
||||
else:
|
||||
gps_bytes = b""
|
||||
gps_length = 0
|
||||
if interop_is:
|
||||
offset = zeroth_length + exif_length + gps_length
|
||||
interop_set = _dict_to_bytes(interop_ifd, "Interop", offset)
|
||||
interop_bytes = b"".join(interop_set)
|
||||
interop_length = len(interop_bytes)
|
||||
else:
|
||||
interop_bytes = b""
|
||||
interop_length = 0
|
||||
if first_is:
|
||||
offset = zeroth_length + exif_length + gps_length + interop_length
|
||||
first_set = _dict_to_bytes(first_ifd, "1st", offset)
|
||||
thumbnail = _get_thumbnail(exif_dict["thumbnail"])
|
||||
thumbnail_max_size = 64000
|
||||
if len(thumbnail) > thumbnail_max_size:
|
||||
raise ValueError("Given thumbnail is too large. max 64kB")
|
||||
else:
|
||||
first_bytes = b""
|
||||
if exif_is:
|
||||
pointer_value = TIFF_HEADER_LENGTH + zeroth_length
|
||||
pointer_str = struct.pack(">I", pointer_value)
|
||||
key = ImageIFD.ExifTag
|
||||
key_str = struct.pack(">H", key)
|
||||
type_str = struct.pack(">H", TYPES.Long)
|
||||
length_str = struct.pack(">I", 1)
|
||||
exif_pointer = key_str + type_str + length_str + pointer_str
|
||||
else:
|
||||
exif_pointer = b""
|
||||
if gps_is:
|
||||
pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length
|
||||
pointer_str = struct.pack(">I", pointer_value)
|
||||
key = ImageIFD.GPSTag
|
||||
key_str = struct.pack(">H", key)
|
||||
type_str = struct.pack(">H", TYPES.Long)
|
||||
length_str = struct.pack(">I", 1)
|
||||
gps_pointer = key_str + type_str + length_str + pointer_str
|
||||
else:
|
||||
gps_pointer = b""
|
||||
if interop_is:
|
||||
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)
|
||||
type_str = struct.pack(">H", TYPES.Long)
|
||||
length_str = struct.pack(">I", 1)
|
||||
interop_pointer = key_str + type_str + length_str + pointer_str
|
||||
else:
|
||||
interop_pointer = b""
|
||||
if first_is:
|
||||
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)
|
||||
else:
|
||||
first_ifd_pointer = b"\x00\x00\x00\x00"
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def _get_thumbnail(jpeg):
|
||||
segments = split_into_segments(jpeg)
|
||||
while (b"\xff\xe0" <= segments[1][0:2] <= b"\xff\xef"):
|
||||
segments.pop(1)
|
||||
thumbnail = b"".join(segments)
|
||||
return thumbnail
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def _value_to_bytes(raw_value, value_type, offset):
|
||||
four_bytes_over = b""
|
||||
value_str = b""
|
||||
|
||||
if value_type == TYPES.Byte:
|
||||
length = len(raw_value)
|
||||
if length <= 4:
|
||||
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))
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_short(*raw_value)
|
||||
elif value_type == TYPES.Long:
|
||||
length = len(raw_value)
|
||||
if length <= 1:
|
||||
value_str = _pack_long(*raw_value)
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_long(*raw_value)
|
||||
elif value_type == TYPES.SLong:
|
||||
length = len(raw_value)
|
||||
if length <= 1:
|
||||
value_str = _pack_slong(*raw_value)
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_slong(*raw_value)
|
||||
elif value_type == TYPES.Ascii:
|
||||
try:
|
||||
new_value = raw_value.encode("latin1") + b"\x00"
|
||||
except:
|
||||
try:
|
||||
new_value = raw_value + b"\x00"
|
||||
except TypeError:
|
||||
raise ValueError("Got invalid type to convert.")
|
||||
length = len(new_value)
|
||||
if length > 4:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = new_value
|
||||
else:
|
||||
value_str = new_value + b"\x00" * (4 - length)
|
||||
elif value_type == TYPES.Rational:
|
||||
if isinstance(raw_value[0], numbers.Integral):
|
||||
length = 1
|
||||
num, den = raw_value
|
||||
new_value = struct.pack(">L", num) + struct.pack(">L", den)
|
||||
elif isinstance(raw_value[0], tuple):
|
||||
length = len(raw_value)
|
||||
new_value = b""
|
||||
for n, val in enumerate(raw_value):
|
||||
num, den = val
|
||||
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:
|
||||
if isinstance(raw_value[0], numbers.Integral):
|
||||
length = 1
|
||||
num, den = raw_value
|
||||
new_value = struct.pack(">l", num) + struct.pack(">l", den)
|
||||
elif isinstance(raw_value[0], tuple):
|
||||
length = len(raw_value)
|
||||
new_value = b""
|
||||
for n, val in enumerate(raw_value):
|
||||
num, den = val
|
||||
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:
|
||||
length = len(raw_value)
|
||||
if length > 4:
|
||||
value_str = struct.pack(">I", offset)
|
||||
try:
|
||||
four_bytes_over = b"" + raw_value
|
||||
except TypeError:
|
||||
raise ValueError("Got invalid type to convert.")
|
||||
else:
|
||||
try:
|
||||
value_str = raw_value + b"\x00" * (4 - length)
|
||||
except TypeError:
|
||||
raise ValueError("Got invalid type to convert.")
|
||||
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))
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_signed_byte(*raw_value)
|
||||
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))
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_signed_short(*raw_value)
|
||||
elif value_type == TYPES.Float:
|
||||
length = len(raw_value)
|
||||
if length <= 1:
|
||||
value_str = _pack_float(*raw_value)
|
||||
else:
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_float(*raw_value)
|
||||
elif value_type == TYPES.DFloat: # Double
|
||||
length = len(raw_value)
|
||||
value_str = struct.pack(">I", offset)
|
||||
four_bytes_over = _pack_double(*raw_value)
|
||||
|
||||
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)
|
||||
if ifd in ("0th", "1st"):
|
||||
entries_length = 2 + tag_count * 12 + 4
|
||||
else:
|
||||
entries_length = 2 + tag_count * 12
|
||||
entries = b""
|
||||
values = b""
|
||||
|
||||
for n, key in enumerate(sorted(ifd_dict)):
|
||||
if (ifd == "0th") and (key in (ImageIFD.ExifTag, ImageIFD.GPSTag)):
|
||||
continue
|
||||
elif (ifd == "Exif") and (key == ExifIFD.InteroperabilityTag):
|
||||
continue
|
||||
elif (ifd == "1st") and (key in (ImageIFD.JPEGInterchangeFormat, ImageIFD.JPEGInterchangeFormatLength)):
|
||||
continue
|
||||
|
||||
raw_value = ifd_dict[key]
|
||||
key_str = struct.pack(">H", key)
|
||||
value_type = TAGS[ifd][key]["type"]
|
||||
type_str = struct.pack(">H", value_type)
|
||||
four_bytes_over = b""
|
||||
|
||||
if isinstance(raw_value, numbers.Integral) or isinstance(raw_value, float):
|
||||
raw_value = (raw_value,)
|
||||
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)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
'"dump" got wrong type of exif value.\n' +
|
||||
'{} in {} IFD. Got as {}.'.format(key, ifd, type(ifd_dict[key]))
|
||||
)
|
||||
|
||||
entries += key_str + type_str + length_str + value_str
|
||||
values += four_bytes_over
|
||||
return (entry_header + entries, values)
|
2
lib/python3.12/site-packages/piexif/_exceptions.py
Normal file
2
lib/python3.12/site-packages/piexif/_exceptions.py
Normal file
@ -0,0 +1,2 @@
|
||||
class InvalidImageDataError(ValueError):
|
||||
pass
|
638
lib/python3.12/site-packages/piexif/_exif.py
Normal file
638
lib/python3.12/site-packages/piexif/_exif.py
Normal file
@ -0,0 +1,638 @@
|
||||
class TYPES:
|
||||
Byte = 1
|
||||
Ascii = 2
|
||||
Short = 3
|
||||
Long = 4
|
||||
Rational = 5
|
||||
SByte = 6
|
||||
Undefined = 7
|
||||
SShort = 8
|
||||
SLong = 9
|
||||
SRational = 10
|
||||
Float = 11
|
||||
DFloat = 12
|
||||
|
||||
|
||||
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}},
|
||||
}
|
||||
|
||||
TAGS["0th"] = TAGS["Image"]
|
||||
TAGS["1st"] = TAGS["Image"]
|
||||
|
||||
class ImageIFD:
|
||||
"""Exif tag number reference - 0th IFD"""
|
||||
ProcessingSoftware = 11
|
||||
NewSubfileType = 254
|
||||
SubfileType = 255
|
||||
ImageWidth = 256
|
||||
ImageLength = 257
|
||||
BitsPerSample = 258
|
||||
Compression = 259
|
||||
PhotometricInterpretation = 262
|
||||
Threshholding = 263
|
||||
CellWidth = 264
|
||||
CellLength = 265
|
||||
FillOrder = 266
|
||||
DocumentName = 269
|
||||
ImageDescription = 270
|
||||
Make = 271
|
||||
Model = 272
|
||||
StripOffsets = 273
|
||||
Orientation = 274
|
||||
SamplesPerPixel = 277
|
||||
RowsPerStrip = 278
|
||||
StripByteCounts = 279
|
||||
XResolution = 282
|
||||
YResolution = 283
|
||||
PlanarConfiguration = 284
|
||||
GrayResponseUnit = 290
|
||||
GrayResponseCurve = 291
|
||||
T4Options = 292
|
||||
T6Options = 293
|
||||
ResolutionUnit = 296
|
||||
TransferFunction = 301
|
||||
Software = 305
|
||||
DateTime = 306
|
||||
Artist = 315
|
||||
HostComputer = 316
|
||||
Predictor = 317
|
||||
WhitePoint = 318
|
||||
PrimaryChromaticities = 319
|
||||
ColorMap = 320
|
||||
HalftoneHints = 321
|
||||
TileWidth = 322
|
||||
TileLength = 323
|
||||
TileOffsets = 324
|
||||
TileByteCounts = 325
|
||||
SubIFDs = 330
|
||||
InkSet = 332
|
||||
InkNames = 333
|
||||
NumberOfInks = 334
|
||||
DotRange = 336
|
||||
TargetPrinter = 337
|
||||
ExtraSamples = 338
|
||||
SampleFormat = 339
|
||||
SMinSampleValue = 340
|
||||
SMaxSampleValue = 341
|
||||
TransferRange = 342
|
||||
ClipPath = 343
|
||||
XClipPathUnits = 344
|
||||
YClipPathUnits = 345
|
||||
Indexed = 346
|
||||
JPEGTables = 347
|
||||
OPIProxy = 351
|
||||
JPEGProc = 512
|
||||
JPEGInterchangeFormat = 513
|
||||
JPEGInterchangeFormatLength = 514
|
||||
JPEGRestartInterval = 515
|
||||
JPEGLosslessPredictors = 517
|
||||
JPEGPointTransforms = 518
|
||||
JPEGQTables = 519
|
||||
JPEGDCTables = 520
|
||||
JPEGACTables = 521
|
||||
YCbCrCoefficients = 529
|
||||
YCbCrSubSampling = 530
|
||||
YCbCrPositioning = 531
|
||||
ReferenceBlackWhite = 532
|
||||
XMLPacket = 700
|
||||
Rating = 18246
|
||||
RatingPercent = 18249
|
||||
ImageID = 32781
|
||||
CFARepeatPatternDim = 33421
|
||||
CFAPattern = 33422
|
||||
BatteryLevel = 33423
|
||||
Copyright = 33432
|
||||
ExposureTime = 33434
|
||||
ImageResources = 34377
|
||||
ExifTag = 34665
|
||||
InterColorProfile = 34675
|
||||
GPSTag = 34853
|
||||
Interlace = 34857
|
||||
TimeZoneOffset = 34858
|
||||
SelfTimerMode = 34859
|
||||
FlashEnergy = 37387
|
||||
SpatialFrequencyResponse = 37388
|
||||
Noise = 37389
|
||||
FocalPlaneXResolution = 37390
|
||||
FocalPlaneYResolution = 37391
|
||||
FocalPlaneResolutionUnit = 37392
|
||||
ImageNumber = 37393
|
||||
SecurityClassification = 37394
|
||||
ImageHistory = 37395
|
||||
ExposureIndex = 37397
|
||||
TIFFEPStandardID = 37398
|
||||
SensingMethod = 37399
|
||||
XPTitle = 40091
|
||||
XPComment = 40092
|
||||
XPAuthor = 40093
|
||||
XPKeywords = 40094
|
||||
XPSubject = 40095
|
||||
PrintImageMatching = 50341
|
||||
DNGVersion = 50706
|
||||
DNGBackwardVersion = 50707
|
||||
UniqueCameraModel = 50708
|
||||
LocalizedCameraModel = 50709
|
||||
CFAPlaneColor = 50710
|
||||
CFALayout = 50711
|
||||
LinearizationTable = 50712
|
||||
BlackLevelRepeatDim = 50713
|
||||
BlackLevel = 50714
|
||||
BlackLevelDeltaH = 50715
|
||||
BlackLevelDeltaV = 50716
|
||||
WhiteLevel = 50717
|
||||
DefaultScale = 50718
|
||||
DefaultCropOrigin = 50719
|
||||
DefaultCropSize = 50720
|
||||
ColorMatrix1 = 50721
|
||||
ColorMatrix2 = 50722
|
||||
CameraCalibration1 = 50723
|
||||
CameraCalibration2 = 50724
|
||||
ReductionMatrix1 = 50725
|
||||
ReductionMatrix2 = 50726
|
||||
AnalogBalance = 50727
|
||||
AsShotNeutral = 50728
|
||||
AsShotWhiteXY = 50729
|
||||
BaselineExposure = 50730
|
||||
BaselineNoise = 50731
|
||||
BaselineSharpness = 50732
|
||||
BayerGreenSplit = 50733
|
||||
LinearResponseLimit = 50734
|
||||
CameraSerialNumber = 50735
|
||||
LensInfo = 50736
|
||||
ChromaBlurRadius = 50737
|
||||
AntiAliasStrength = 50738
|
||||
ShadowScale = 50739
|
||||
DNGPrivateData = 50740
|
||||
MakerNoteSafety = 50741
|
||||
CalibrationIlluminant1 = 50778
|
||||
CalibrationIlluminant2 = 50779
|
||||
BestQualityScale = 50780
|
||||
RawDataUniqueID = 50781
|
||||
OriginalRawFileName = 50827
|
||||
OriginalRawFileData = 50828
|
||||
ActiveArea = 50829
|
||||
MaskedAreas = 50830
|
||||
AsShotICCProfile = 50831
|
||||
AsShotPreProfileMatrix = 50832
|
||||
CurrentICCProfile = 50833
|
||||
CurrentPreProfileMatrix = 50834
|
||||
ColorimetricReference = 50879
|
||||
CameraCalibrationSignature = 50931
|
||||
ProfileCalibrationSignature = 50932
|
||||
AsShotProfileName = 50934
|
||||
NoiseReductionApplied = 50935
|
||||
ProfileName = 50936
|
||||
ProfileHueSatMapDims = 50937
|
||||
ProfileHueSatMapData1 = 50938
|
||||
ProfileHueSatMapData2 = 50939
|
||||
ProfileToneCurve = 50940
|
||||
ProfileEmbedPolicy = 50941
|
||||
ProfileCopyright = 50942
|
||||
ForwardMatrix1 = 50964
|
||||
ForwardMatrix2 = 50965
|
||||
PreviewApplicationName = 50966
|
||||
PreviewApplicationVersion = 50967
|
||||
PreviewSettingsName = 50968
|
||||
PreviewSettingsDigest = 50969
|
||||
PreviewColorSpace = 50970
|
||||
PreviewDateTime = 50971
|
||||
RawImageDigest = 50972
|
||||
OriginalRawFileDigest = 50973
|
||||
SubTileBlockSize = 50974
|
||||
RowInterleaveFactor = 50975
|
||||
ProfileLookTableDims = 50981
|
||||
ProfileLookTableData = 50982
|
||||
OpcodeList1 = 51008
|
||||
OpcodeList2 = 51009
|
||||
OpcodeList3 = 51022
|
||||
NoiseProfile = 51041
|
||||
ZZZTestSlong1 = 60606
|
||||
ZZZTestSlong2 = 60607
|
||||
ZZZTestSByte = 60608
|
||||
ZZZTestSShort = 60609
|
||||
ZZZTestDFloat = 60610
|
||||
|
||||
|
||||
class ExifIFD:
|
||||
"""Exif tag number reference - Exif IFD"""
|
||||
ExposureTime = 33434
|
||||
FNumber = 33437
|
||||
ExposureProgram = 34850
|
||||
SpectralSensitivity = 34852
|
||||
ISOSpeedRatings = 34855
|
||||
OECF = 34856
|
||||
SensitivityType = 34864
|
||||
StandardOutputSensitivity = 34865
|
||||
RecommendedExposureIndex = 34866
|
||||
ISOSpeed = 34867
|
||||
ISOSpeedLatitudeyyy = 34868
|
||||
ISOSpeedLatitudezzz = 34869
|
||||
ExifVersion = 36864
|
||||
DateTimeOriginal = 36867
|
||||
DateTimeDigitized = 36868
|
||||
OffsetTime = 36880
|
||||
OffsetTimeOriginal = 36881
|
||||
OffsetTimeDigitized = 36882
|
||||
ComponentsConfiguration = 37121
|
||||
CompressedBitsPerPixel = 37122
|
||||
ShutterSpeedValue = 37377
|
||||
ApertureValue = 37378
|
||||
BrightnessValue = 37379
|
||||
ExposureBiasValue = 37380
|
||||
MaxApertureValue = 37381
|
||||
SubjectDistance = 37382
|
||||
MeteringMode = 37383
|
||||
LightSource = 37384
|
||||
Flash = 37385
|
||||
FocalLength = 37386
|
||||
Temperature = 37888
|
||||
Humidity = 37889
|
||||
Pressure = 37890
|
||||
WaterDepth = 37891
|
||||
Acceleration = 37892
|
||||
CameraElevationAngle = 37893
|
||||
SubjectArea = 37396
|
||||
MakerNote = 37500
|
||||
UserComment = 37510
|
||||
SubSecTime = 37520
|
||||
SubSecTimeOriginal = 37521
|
||||
SubSecTimeDigitized = 37522
|
||||
FlashpixVersion = 40960
|
||||
ColorSpace = 40961
|
||||
PixelXDimension = 40962
|
||||
PixelYDimension = 40963
|
||||
RelatedSoundFile = 40964
|
||||
InteroperabilityTag = 40965
|
||||
FlashEnergy = 41483
|
||||
SpatialFrequencyResponse = 41484
|
||||
FocalPlaneXResolution = 41486
|
||||
FocalPlaneYResolution = 41487
|
||||
FocalPlaneResolutionUnit = 41488
|
||||
SubjectLocation = 41492
|
||||
ExposureIndex = 41493
|
||||
SensingMethod = 41495
|
||||
FileSource = 41728
|
||||
SceneType = 41729
|
||||
CFAPattern = 41730
|
||||
CustomRendered = 41985
|
||||
ExposureMode = 41986
|
||||
WhiteBalance = 41987
|
||||
DigitalZoomRatio = 41988
|
||||
FocalLengthIn35mmFilm = 41989
|
||||
SceneCaptureType = 41990
|
||||
GainControl = 41991
|
||||
Contrast = 41992
|
||||
Saturation = 41993
|
||||
Sharpness = 41994
|
||||
DeviceSettingDescription = 41995
|
||||
SubjectDistanceRange = 41996
|
||||
ImageUniqueID = 42016
|
||||
CameraOwnerName = 42032
|
||||
BodySerialNumber = 42033
|
||||
LensSpecification = 42034
|
||||
LensMake = 42035
|
||||
LensModel = 42036
|
||||
LensSerialNumber = 42037
|
||||
Gamma = 42240
|
||||
|
||||
|
||||
class GPSIFD:
|
||||
"""Exif tag number reference - GPS IFD"""
|
||||
GPSVersionID = 0
|
||||
GPSLatitudeRef = 1
|
||||
GPSLatitude = 2
|
||||
GPSLongitudeRef = 3
|
||||
GPSLongitude = 4
|
||||
GPSAltitudeRef = 5
|
||||
GPSAltitude = 6
|
||||
GPSTimeStamp = 7
|
||||
GPSSatellites = 8
|
||||
GPSStatus = 9
|
||||
GPSMeasureMode = 10
|
||||
GPSDOP = 11
|
||||
GPSSpeedRef = 12
|
||||
GPSSpeed = 13
|
||||
GPSTrackRef = 14
|
||||
GPSTrack = 15
|
||||
GPSImgDirectionRef = 16
|
||||
GPSImgDirection = 17
|
||||
GPSMapDatum = 18
|
||||
GPSDestLatitudeRef = 19
|
||||
GPSDestLatitude = 20
|
||||
GPSDestLongitudeRef = 21
|
||||
GPSDestLongitude = 22
|
||||
GPSDestBearingRef = 23
|
||||
GPSDestBearing = 24
|
||||
GPSDestDistanceRef = 25
|
||||
GPSDestDistance = 26
|
||||
GPSProcessingMethod = 27
|
||||
GPSAreaInformation = 28
|
||||
GPSDateStamp = 29
|
||||
GPSDifferential = 30
|
||||
GPSHPositioningError = 31
|
||||
|
||||
|
||||
class InteropIFD:
|
||||
"""Exif tag number reference - Interoperability IFD"""
|
||||
InteroperabilityIndex = 1
|
60
lib/python3.12/site-packages/piexif/_insert.py
Normal file
60
lib/python3.12/site-packages/piexif/_insert.py
Normal file
@ -0,0 +1,60 @@
|
||||
import io
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from ._common import *
|
||||
from ._exceptions import InvalidImageDataError
|
||||
from piexif import _webp
|
||||
|
||||
def insert(exif, image, new_file=None):
|
||||
"""
|
||||
py:function:: piexif.insert(exif_bytes, filename)
|
||||
|
||||
Insert exif into JPEG.
|
||||
|
||||
:param bytes exif_bytes: Exif as bytes
|
||||
:param str filename: JPEG
|
||||
"""
|
||||
if exif[0:6] != b"\x45\x78\x69\x66\x00\x00":
|
||||
raise ValueError("Given data is not exif data")
|
||||
|
||||
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)
|
||||
|
||||
if maybe_image and image[0:2] == b"\xff\xd8":
|
||||
image_data = image
|
||||
file_type = "jpeg"
|
||||
elif maybe_image and image[0:4] == b"RIFF" and image[8:12] == b"WEBP":
|
||||
image_data = image
|
||||
file_type = "webp"
|
||||
else:
|
||||
with open(image, 'rb') as f:
|
||||
image_data = f.read()
|
||||
if image_data[0:2] == b"\xff\xd8":
|
||||
file_type = "jpeg"
|
||||
elif image_data[0:4] == b"RIFF" and image_data[8:12] == b"WEBP":
|
||||
file_type = "webp"
|
||||
else:
|
||||
raise InvalidImageDataError
|
||||
output_file = True
|
||||
|
||||
if file_type == "jpeg":
|
||||
exif = b"\xff\xe1" + struct.pack(">H", len(exif) + 2) + exif
|
||||
segments = split_into_segments(image_data)
|
||||
new_data = merge_segments(segments, exif)
|
||||
elif file_type == "webp":
|
||||
exif = exif[6:]
|
||||
new_data = _webp.insert(image_data, exif)
|
||||
|
||||
if isinstance(new_file, io.BytesIO):
|
||||
new_file.write(new_data)
|
||||
new_file.seek(0)
|
||||
elif new_file:
|
||||
with open(new_file, "wb+") as f:
|
||||
f.write(new_data)
|
||||
elif output_file:
|
||||
with open(image, "wb+") as f:
|
||||
f.write(new_data)
|
||||
else:
|
||||
raise ValueError("Give a 3rd argument to 'insert' to output file")
|
270
lib/python3.12/site-packages/piexif/_load.py
Normal file
270
lib/python3.12/site-packages/piexif/_load.py
Normal file
@ -0,0 +1,270 @@
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from ._common import *
|
||||
from ._exceptions import InvalidImageDataError
|
||||
from ._exif import *
|
||||
from piexif import _webp
|
||||
|
||||
LITTLE_ENDIAN = b"\x49\x49"
|
||||
|
||||
|
||||
def load(input_data, key_is_name=False):
|
||||
"""
|
||||
py:function:: piexif.load(filename)
|
||||
|
||||
Return exif data as dict. Keys(IFD name), be contained, are "0th", "Exif", "GPS", "Interop", "1st", and "thumbnail". Without "thumbnail", the value is dict(tag name/tag value). "thumbnail" value is JPEG as bytes.
|
||||
|
||||
:param str filename: JPEG or TIFF
|
||||
: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}
|
||||
exifReader = _ExifReader(input_data)
|
||||
if exifReader.tiftag is None:
|
||||
return exif_dict
|
||||
|
||||
if exifReader.tiftag[0:2] == LITTLE_ENDIAN:
|
||||
exifReader.endian_mark = "<"
|
||||
else:
|
||||
exifReader.endian_mark = ">"
|
||||
|
||||
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"]:
|
||||
pointer = exif_dict["0th"][ImageIFD.ExifTag]
|
||||
exif_dict["Exif"] = exifReader.get_ifd_dict(pointer, "Exif")
|
||||
if ImageIFD.GPSTag in exif_dict["0th"]:
|
||||
pointer = exif_dict["0th"][ImageIFD.GPSTag]
|
||||
exif_dict["GPS"] = exifReader.get_ifd_dict(pointer, "GPS")
|
||||
if ExifIFD.InteroperabilityTag in exif_dict["Exif"]:
|
||||
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]
|
||||
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]
|
||||
exif_dict["thumbnail"] = thumb
|
||||
|
||||
if key_is_name:
|
||||
exif_dict = _get_key_name_dict(exif_dict)
|
||||
return exif_dict
|
||||
|
||||
|
||||
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)
|
||||
|
||||
if maybe_image and data[0:2] == b"\xff\xd8": # JPEG
|
||||
segments = split_into_segments(data)
|
||||
app1 = get_exif_seg(segments)
|
||||
if app1:
|
||||
self.tiftag = app1[10:]
|
||||
else:
|
||||
self.tiftag = None
|
||||
elif maybe_image and data[0:2] in (b"\x49\x49", b"\x4d\x4d"): # TIFF
|
||||
self.tiftag = data
|
||||
elif maybe_image and data[0:4] == b"RIFF" and data[8:12] == b"WEBP":
|
||||
self.tiftag = _webp.get_exif(data)
|
||||
elif maybe_image and data[0:4] == b"Exif": # Exif
|
||||
self.tiftag = data[6:]
|
||||
else:
|
||||
with open(data, 'rb') as f:
|
||||
magic_number = f.read(2)
|
||||
if magic_number == b"\xff\xd8": # JPEG
|
||||
app1 = read_exif_from_file(data)
|
||||
if app1:
|
||||
self.tiftag = app1[10:]
|
||||
else:
|
||||
self.tiftag = None
|
||||
elif magic_number in (b"\x49\x49", b"\x4d\x4d"): # TIFF
|
||||
with open(data, 'rb') as f:
|
||||
self.tiftag = f.read()
|
||||
else:
|
||||
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:
|
||||
file_data = f.read()
|
||||
self.tiftag = _webp.get_exif(file_data)
|
||||
else:
|
||||
raise InvalidImageDataError("Given file is neither JPEG nor TIFF.")
|
||||
|
||||
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]
|
||||
offset = pointer + 2
|
||||
if ifd_name in ["0th", "1st"]:
|
||||
t = "Image"
|
||||
else:
|
||||
t = ifd_name
|
||||
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]
|
||||
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:
|
||||
# pass
|
||||
|
||||
if ifd_name == "0th":
|
||||
pointer = offset + 12 * tag_count
|
||||
ifd_dict["first_ifd_pointer"] = self.tiftag[pointer:pointer + 4]
|
||||
return ifd_dict
|
||||
|
||||
def convert_value(self, val):
|
||||
data = None
|
||||
t = val[0]
|
||||
length = val[1]
|
||||
value = val[2]
|
||||
|
||||
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])
|
||||
else:
|
||||
data = struct.unpack("B" * length, value[0:length])
|
||||
elif t == TYPES.Ascii: # ASCII
|
||||
if length > 4:
|
||||
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
||||
data = self.tiftag[pointer: pointer+length - 1]
|
||||
else:
|
||||
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])
|
||||
else:
|
||||
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])
|
||||
else:
|
||||
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])
|
||||
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
|
||||
if length > 4:
|
||||
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
||||
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
|
||||
if length > 4:
|
||||
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
||||
data = self.tiftag[pointer: pointer+length]
|
||||
else:
|
||||
data = value[0:length]
|
||||
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])
|
||||
else:
|
||||
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])
|
||||
else:
|
||||
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)
|
||||
)
|
||||
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
|
||||
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])
|
||||
else:
|
||||
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])
|
||||
else:
|
||||
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]
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
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"],
|
||||
}
|
||||
return new_dict
|
56
lib/python3.12/site-packages/piexif/_remove.py
Normal file
56
lib/python3.12/site-packages/piexif/_remove.py
Normal file
@ -0,0 +1,56 @@
|
||||
import io
|
||||
|
||||
from ._common import *
|
||||
from piexif import _webp
|
||||
|
||||
def remove(src, new_file=None):
|
||||
"""
|
||||
py:function:: piexif.remove(filename)
|
||||
|
||||
Remove exif from JPEG.
|
||||
|
||||
:param str filename: JPEG
|
||||
"""
|
||||
output_is_file = False
|
||||
if src[0:2] == b"\xff\xd8":
|
||||
src_data = src
|
||||
file_type = "jpeg"
|
||||
elif src[0:4] == b"RIFF" and src[8:12] == b"WEBP":
|
||||
src_data = src
|
||||
file_type = "webp"
|
||||
else:
|
||||
with open(src, 'rb') as f:
|
||||
src_data = f.read()
|
||||
output_is_file = True
|
||||
if src_data[0:2] == b"\xff\xd8":
|
||||
file_type = "jpeg"
|
||||
elif src_data[0:4] == b"RIFF" and src_data[8:12] == b"WEBP":
|
||||
file_type = "webp"
|
||||
|
||||
if file_type == "jpeg":
|
||||
segments = split_into_segments(src_data)
|
||||
exif = get_exif_seg(segments)
|
||||
if exif:
|
||||
new_data = src_data.replace(exif, b"")
|
||||
else:
|
||||
new_data = src_data
|
||||
elif file_type == "webp":
|
||||
try:
|
||||
new_data = _webp.remove(src_data)
|
||||
except ValueError:
|
||||
new_data = src_data
|
||||
except e:
|
||||
print(e.args)
|
||||
raise ValueError("Error occurred.")
|
||||
|
||||
if isinstance(new_file, io.BytesIO):
|
||||
new_file.write(new_data)
|
||||
new_file.seek(0)
|
||||
elif new_file:
|
||||
with open(new_file, "wb+") as f:
|
||||
f.write(new_data)
|
||||
elif output_is_file:
|
||||
with open(src, "wb+") as f:
|
||||
f.write(new_data)
|
||||
else:
|
||||
raise ValueError("Give a second argument to 'remove' to output file")
|
45
lib/python3.12/site-packages/piexif/_transplant.py
Normal file
45
lib/python3.12/site-packages/piexif/_transplant.py
Normal file
@ -0,0 +1,45 @@
|
||||
import io
|
||||
|
||||
from ._common import *
|
||||
|
||||
|
||||
def transplant(exif_src, image, new_file=None):
|
||||
"""
|
||||
py:function:: piexif.transplant(filename1, filename2)
|
||||
|
||||
Transplant exif from filename1 to filename2.
|
||||
|
||||
:param str filename1: JPEG
|
||||
:param str filename2: JPEG
|
||||
"""
|
||||
if exif_src[0:2] == b"\xff\xd8":
|
||||
src_data = exif_src
|
||||
else:
|
||||
with open(exif_src, 'rb') as f:
|
||||
src_data = f.read()
|
||||
segments = split_into_segments(src_data)
|
||||
exif = get_exif_seg(segments)
|
||||
if exif is None:
|
||||
raise ValueError("not found exif in input")
|
||||
|
||||
output_file = False
|
||||
if image[0:2] == b"\xff\xd8":
|
||||
image_data = image
|
||||
else:
|
||||
with open(image, 'rb') as f:
|
||||
image_data = f.read()
|
||||
output_file = True
|
||||
segments = split_into_segments(image_data)
|
||||
new_data = merge_segments(segments, exif)
|
||||
|
||||
if isinstance(new_file, io.BytesIO):
|
||||
new_file.write(new_data)
|
||||
new_file.seek(0)
|
||||
elif new_file:
|
||||
with open(new_file, "wb+") as f:
|
||||
f.write(new_data)
|
||||
elif output_file:
|
||||
with open(image, "wb+") as f:
|
||||
f.write(new_data)
|
||||
else:
|
||||
raise ValueError("Give a 3rd argument to 'transplant' to output file")
|
240
lib/python3.12/site-packages/piexif/_webp.py
Normal file
240
lib/python3.12/site-packages/piexif/_webp.py
Normal file
@ -0,0 +1,240 @@
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
def split(data):
|
||||
if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
|
||||
raise ValueError("Not WebP")
|
||||
|
||||
webp_length_bytes = data[4:8]
|
||||
webp_length = struct.unpack("<L", webp_length_bytes)[0]
|
||||
RIFF_HEADER_SIZE = 8
|
||||
file_size = RIFF_HEADER_SIZE + webp_length
|
||||
|
||||
start = 12
|
||||
pointer = start
|
||||
CHUNK_FOURCC_LENGTH = 4
|
||||
LENGTH_BYTES_LENGTH = 4
|
||||
|
||||
chunks = []
|
||||
while pointer + CHUNK_FOURCC_LENGTH + LENGTH_BYTES_LENGTH < file_size:
|
||||
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
|
||||
pointer += CHUNK_FOURCC_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})
|
||||
|
||||
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])
|
||||
return merged
|
||||
|
||||
|
||||
def _get_size_from_vp8x(chunk):
|
||||
width_minus_one_bytes = chunk["data"][-6:-3] + b"\x00"
|
||||
width_minus_one = struct.unpack("<L", width_minus_one_bytes)[0]
|
||||
width = width_minus_one + 1
|
||||
height_minus_one_bytes = chunk["data"][-3:] + b"\x00"
|
||||
height_minus_one = struct.unpack("<L", height_minus_one_bytes)[0]
|
||||
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)
|
||||
if begin_index == -1:
|
||||
ValueError("wrong VP8")
|
||||
else:
|
||||
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 = struct.unpack("<H", width_bytes)[0]
|
||||
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")
|
||||
contains = 1 * flag
|
||||
return contains
|
||||
|
||||
def _get_size_from_vp8L(chunk):
|
||||
b1 = chunk["data"][1:2]
|
||||
b2 = chunk["data"][2:3]
|
||||
b3 = chunk["data"][3:4]
|
||||
b4 = chunk["data"][4:5]
|
||||
|
||||
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 = 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]
|
||||
width = width_minus_one + 1
|
||||
height_minus_one_bytes = chunk["data"][9:12] + b"\x00"
|
||||
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]
|
||||
|
||||
for chunk in chunks:
|
||||
if chunk["fourcc"] == b"VP8X":
|
||||
width, height = _get_size_from_vp8x(chunk)
|
||||
elif chunk["fourcc"] == b"VP8 ":
|
||||
width, height = _get_size_from_vp8(chunk)
|
||||
elif chunk["fourcc"] == b"VP8L":
|
||||
is_rgba = _vp8L_contains_alpha(chunk["data"])
|
||||
if is_rgba:
|
||||
flags[3] = b"1"
|
||||
width, height = _get_size_from_vp8L(chunk)
|
||||
elif chunk["fourcc"] == b"ANMF":
|
||||
width, height = _get_size_from_anmf(chunk)
|
||||
elif chunk["fourcc"] == b"ICCP":
|
||||
flags[2] = b"1"
|
||||
elif chunk["fourcc"] == b"ALPH":
|
||||
flags[3] = b"1"
|
||||
elif chunk["fourcc"] == b"EXIF":
|
||||
flags[4] = b"1"
|
||||
elif chunk["fourcc"] == b"XMP ":
|
||||
flags[5] = b"1"
|
||||
elif chunk["fourcc"] == b"ANIM":
|
||||
flags[6] = b"1"
|
||||
width_minus_one = width - 1
|
||||
height_minus_one = height - 1
|
||||
|
||||
if chunks[0]["fourcc"] == b"VP8X":
|
||||
chunks.pop(0)
|
||||
|
||||
header_bytes = b"VP8X"
|
||||
length_bytes = b"\x0a\x00\x00\x00"
|
||||
flags_bytes = struct.pack("B", int(b"".join(flags), 2))
|
||||
padding_bytes = b"\x00\x00\x00"
|
||||
width_bytes = struct.pack("<L", width_minus_one)[:3]
|
||||
height_bytes = struct.pack("<L", height_minus_one)[:3]
|
||||
|
||||
data_bytes = flags_bytes + padding_bytes + width_bytes + height_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
|
||||
LENGTH_BYTES_LENGTH = 4
|
||||
|
||||
length = WEBP_HEADER_LENGTH
|
||||
for chunk in chunks:
|
||||
data_length = struct.unpack("<L", chunk["length_bytes"])[0]
|
||||
data_length += 1 if data_length % 2 else 0
|
||||
length += FOURCC_LENGTH + LENGTH_BYTES_LENGTH + data_length
|
||||
length_bytes = struct.pack("<L", length)
|
||||
riff = b"RIFF"
|
||||
webp_header = b"WEBP"
|
||||
file_header = riff + length_bytes + webp_header
|
||||
return file_header
|
||||
|
||||
|
||||
|
||||
def get_exif(data):
|
||||
if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
|
||||
raise ValueError("Not WebP")
|
||||
|
||||
if data[12:16] != b"VP8X":
|
||||
raise ValueError("doesnot have exif")
|
||||
|
||||
webp_length_bytes = data[4:8]
|
||||
webp_length = struct.unpack("<L", webp_length_bytes)[0]
|
||||
RIFF_HEADER_SIZE = 8
|
||||
file_size = RIFF_HEADER_SIZE + webp_length
|
||||
|
||||
start = 12
|
||||
pointer = start
|
||||
CHUNK_FOURCC_LENGTH = 4
|
||||
LENGTH_BYTES_LENGTH = 4
|
||||
|
||||
chunks = []
|
||||
exif = b""
|
||||
while pointer < file_size:
|
||||
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
|
||||
pointer += CHUNK_FOURCC_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]
|
||||
pointer += chunk_length
|
||||
return None # if there isn't exif, return None.
|
||||
|
||||
|
||||
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}
|
||||
|
||||
xmp_index = None
|
||||
animation_index = None
|
||||
|
||||
for index, chunk in enumerate(chunks):
|
||||
if chunk["fourcc"] == b"EXIF":
|
||||
chunks.pop(index)
|
||||
|
||||
for index, chunk in enumerate(chunks):
|
||||
if chunk["fourcc"] == b"XMP ":
|
||||
xmp_index = index
|
||||
elif chunk["fourcc"] == b"ANIM":
|
||||
animation_index = index
|
||||
if xmp_index is not None:
|
||||
chunks.insert(xmp_index, exif_chunk)
|
||||
elif animation_index is not None:
|
||||
chunks.insert(animation_index, exif_chunk)
|
||||
else:
|
||||
chunks.append(exif_chunk)
|
||||
return chunks
|
||||
|
||||
|
||||
def insert(webp_bytes, exif_bytes):
|
||||
chunks = split(webp_bytes)
|
||||
chunks = insert_exif_into_chunks(chunks, exif_bytes)
|
||||
chunks = set_vp8x(chunks)
|
||||
file_header = get_file_header(chunks)
|
||||
merged = merge_chunks(chunks)
|
||||
new_webp_bytes = file_header + merged
|
||||
return new_webp_bytes
|
||||
|
||||
|
||||
def remove(webp_bytes):
|
||||
chunks = split(webp_bytes)
|
||||
for index, chunk in enumerate(chunks):
|
||||
if chunk["fourcc"] == b"EXIF":
|
||||
chunks.pop(index)
|
||||
chunks = set_vp8x(chunks)
|
||||
file_header = get_file_header(chunks)
|
||||
merged = merge_chunks(chunks)
|
||||
new_webp_bytes = file_header + merged
|
||||
return new_webp_bytes
|
66
lib/python3.12/site-packages/piexif/helper.py
Normal file
66
lib/python3.12/site-packages/piexif/helper.py
Normal file
@ -0,0 +1,66 @@
|
||||
class UserComment:
|
||||
#
|
||||
# Names of encodings that we publicly support.
|
||||
#
|
||||
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'
|
||||
|
||||
_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'
|
||||
|
||||
@classmethod
|
||||
def load(cls, data):
|
||||
"""
|
||||
Convert "UserComment" value in exif format to str.
|
||||
|
||||
:param bytes data: "UserComment" value from exif
|
||||
:return: u"foobar"
|
||||
:rtype: str(Unicode)
|
||||
:raises: ValueError if the data does not conform to the EXIF specification,
|
||||
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:]
|
||||
if prefix == cls._UNDEFINED_PREFIX:
|
||||
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,
|
||||
}[prefix]
|
||||
except KeyError:
|
||||
raise ValueError('unable to determine appropriate encoding')
|
||||
return body.decode(encoding, errors='replace')
|
||||
|
||||
@classmethod
|
||||
def dump(cls, data, encoding="ascii"):
|
||||
"""
|
||||
Convert str to appropriate format for "UserComment".
|
||||
|
||||
:param data: Like u"foobar"
|
||||
:param str encoding: "ascii", "jis", or "unicode"
|
||||
:return: b"ASCII\x00\x00\x00foobar"
|
||||
:rtype: bytes
|
||||
:raises: ValueError if the encoding is unsupported.
|
||||
"""
|
||||
if encoding not in cls.ENCODINGS:
|
||||
raise ValueError('encoding {!r} must be one of {!r}'.format(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')
|
Reference in New Issue
Block a user