feat: Unify Driveのコピー処理を実装
This commit is contained in:
Vendored
+200
@@ -0,0 +1,200 @@
|
||||
"""Extract EXIF from JPEG files."""
|
||||
|
||||
from typing import BinaryIO, Tuple
|
||||
|
||||
from exifread.core.exceptions import InvalidExif
|
||||
from exifread.core.utils import ord_
|
||||
from exifread.exif_log import get_logger
|
||||
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
def _increment_base(data, base) -> int:
|
||||
return ord_(data[base + 2]) * 256 + ord_(data[base + 3]) + 2
|
||||
|
||||
|
||||
def _get_initial_base(fh: BinaryIO, data: bytes, fake_exif: int) -> Tuple[int, int]:
|
||||
base = 2
|
||||
logger.debug(
|
||||
"data[2]=0x%X data[3]=0x%X data[6:10]=%s",
|
||||
ord_(data[2]),
|
||||
ord_(data[3]),
|
||||
data[6:10],
|
||||
)
|
||||
while ord_(data[2]) == 0xFF and data[6:10] in (b"JFIF", b"JFXX", b"OLYM", b"Phot"):
|
||||
length = ord_(data[4]) * 256 + ord_(data[5])
|
||||
logger.debug(" Length offset is %s", length)
|
||||
fh.read(length - 8)
|
||||
# fake an EXIF beginning of file
|
||||
# I don't think this is used. --gd
|
||||
data = b"\xff\x00" + fh.read(10)
|
||||
fake_exif = 1
|
||||
if base > 2:
|
||||
logger.debug(" Added to base")
|
||||
base = base + length + 4 - 2
|
||||
else:
|
||||
logger.debug(" Added to zero")
|
||||
base = length + 4
|
||||
logger.debug(" Set segment base to 0x%X", base)
|
||||
return base, fake_exif
|
||||
|
||||
|
||||
def _get_base(base: int, data: bytes) -> int:
|
||||
# pylint: disable=too-many-statements
|
||||
while True:
|
||||
logger.debug(" Segment base 0x%X", base)
|
||||
if data[base : base + 2] == b"\xff\xe1":
|
||||
# APP1
|
||||
logger.debug(" APP1 at base 0x%X", base)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug(" Code: %s", data[base + 4 : base + 8])
|
||||
if data[base + 4 : base + 8] == b"Exif":
|
||||
logger.debug(
|
||||
" Decrement base by 2 to get to pre-segment header (for compatibility with later code)"
|
||||
)
|
||||
base -= 2
|
||||
break
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
elif data[base : base + 2] == b"\xff\xe0":
|
||||
# APP0
|
||||
logger.debug(" APP0 at base 0x%X", base)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug(" Code: %s", data[base + 4 : base + 8])
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
elif data[base : base + 2] == b"\xff\xe2":
|
||||
# APP2
|
||||
logger.debug(" APP2 at base 0x%X", base)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug(" Code: %s", data[base + 4 : base + 8])
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
elif data[base : base + 2] == b"\xff\xee":
|
||||
# APP14
|
||||
logger.debug(" APP14 Adobe segment at base 0x%X", base)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug(" Code: %s", data[base + 4 : base + 8])
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
logger.debug(
|
||||
" There is useful EXIF-like data here, but we have no parser for it."
|
||||
)
|
||||
elif data[base : base + 2] == b"\xff\xdb":
|
||||
logger.debug(
|
||||
" JPEG image data at base 0x%X No more segments are expected.", base
|
||||
)
|
||||
break
|
||||
elif data[base : base + 2] == b"\xff\xd8":
|
||||
# APP12
|
||||
logger.debug(" FFD8 segment at base 0x%X", base)
|
||||
logger.debug(
|
||||
" Got 0x%X 0x%X and %s instead",
|
||||
ord_(data[base]),
|
||||
ord_(data[base + 1]),
|
||||
data[4 + base : 10 + base],
|
||||
)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug(" Code: %s", data[base + 4 : base + 8])
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
elif data[base : base + 2] == b"\xff\xec":
|
||||
# APP12
|
||||
logger.debug(
|
||||
" APP12 XMP (Ducky) or Pictureinfo segment at base 0x%X", base
|
||||
)
|
||||
logger.debug(
|
||||
" Got 0x%X and 0x%X instead", ord_(data[base]), ord_(data[base + 1])
|
||||
)
|
||||
logger.debug(
|
||||
" Length: 0x%X 0x%X", ord_(data[base + 2]), ord_(data[base + 3])
|
||||
)
|
||||
logger.debug("Code: %s", data[base + 4 : base + 8])
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
logger.debug(
|
||||
(
|
||||
" There is useful EXIF-like data here (quality, comment, copyright), "
|
||||
"but we have no parser for it."
|
||||
)
|
||||
)
|
||||
else:
|
||||
try:
|
||||
increment = _increment_base(data, base)
|
||||
logger.debug(
|
||||
" Got 0x%X and 0x%X instead",
|
||||
ord_(data[base]),
|
||||
ord_(data[base + 1]),
|
||||
)
|
||||
except IndexError as err:
|
||||
raise InvalidExif(
|
||||
"Unexpected/unhandled segment type or file content."
|
||||
) from err
|
||||
logger.debug(" Increment base by %s", increment)
|
||||
base += increment
|
||||
return base
|
||||
|
||||
|
||||
def find_jpeg_exif(fh: BinaryIO, data: bytes, fake_exif: int) -> Tuple[int, bytes, int]:
|
||||
logger.debug(
|
||||
"JPEG format recognized data[0:2]=0x%X%X", ord_(data[0]), ord_(data[1])
|
||||
)
|
||||
|
||||
base, fake_exif = _get_initial_base(fh, data, fake_exif)
|
||||
|
||||
# Big ugly patch to deal with APP2 (or other) data coming before APP1
|
||||
fh.seek(0)
|
||||
# in theory, this could be insufficient since 64K is the maximum size--gd
|
||||
data = fh.read(base + 4000)
|
||||
|
||||
base = _get_base(base, data)
|
||||
|
||||
fh.seek(base + 12)
|
||||
if ord_(data[2 + base]) == 0xFF and data[6 + base : 10 + base] == b"Exif":
|
||||
# detected EXIF header
|
||||
offset = fh.tell()
|
||||
endian = fh.read(1)
|
||||
# HACK TEST: endian = 'M'
|
||||
elif ord_(data[2 + base]) == 0xFF and data[6 + base : 10 + base + 1] == b"Ducky":
|
||||
# detected Ducky header.
|
||||
logger.debug(
|
||||
"EXIF-like header (normally 0xFF and code): 0x%X and %s",
|
||||
ord_(data[2 + base]),
|
||||
data[6 + base : 10 + base + 1],
|
||||
)
|
||||
offset = fh.tell()
|
||||
endian = fh.read(1)
|
||||
elif ord_(data[2 + base]) == 0xFF and data[6 + base : 10 + base + 1] == b"Adobe":
|
||||
# detected APP14 (Adobe)
|
||||
logger.debug(
|
||||
"EXIF-like header (normally 0xFF and code): 0x%X and %s",
|
||||
ord_(data[2 + base]),
|
||||
data[6 + base : 10 + base + 1],
|
||||
)
|
||||
offset = fh.tell()
|
||||
endian = fh.read(1)
|
||||
else:
|
||||
# no EXIF information
|
||||
msg = "No EXIF header expected data[2+base]==0xFF and data[6+base:10+base]===Exif (or Duck)"
|
||||
msg += " Did get 0x%X and %r" % (
|
||||
ord_(data[2 + base]),
|
||||
data[6 + base : 10 + base + 1],
|
||||
)
|
||||
raise InvalidExif(msg)
|
||||
return offset, endian, fake_exif
|
||||
Reference in New Issue
Block a user