sdata 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdata/__init__.py +140 -0
- sdata/base.py +684 -0
- sdata/contrib/__init__.py +0 -0
- sdata/contrib/piexif/__init__.py +12 -0
- sdata/contrib/piexif/_common.py +94 -0
- sdata/contrib/piexif/_dump.py +346 -0
- sdata/contrib/piexif/_exceptions.py +2 -0
- sdata/contrib/piexif/_exif.py +638 -0
- sdata/contrib/piexif/_insert.py +60 -0
- sdata/contrib/piexif/_load.py +270 -0
- sdata/contrib/piexif/_remove.py +56 -0
- sdata/contrib/piexif/_transplant.py +45 -0
- sdata/contrib/piexif/_webp.py +240 -0
- sdata/contrib/piexif/helper.py +66 -0
- sdata/contrib/ranger/__init__.py +8 -0
- sdata/contrib/ranger/collections/__init__.py +0 -0
- sdata/contrib/ranger/collections/rangebucketmap.py +346 -0
- sdata/contrib/ranger/collections/rangemap.py +396 -0
- sdata/contrib/ranger/collections/rangeset.py +483 -0
- sdata/contrib/ranger/range/__init__.py +0 -0
- sdata/contrib/ranger/range/cut.py +285 -0
- sdata/contrib/ranger/range/range.py +644 -0
- sdata/contrib/sobol_seq.py +394 -0
- sdata/contrib/sortedcontainers/LICENSE +13 -0
- sdata/contrib/sortedcontainers/__init__.py +74 -0
- sdata/contrib/sortedcontainers/sorteddict.py +789 -0
- sdata/contrib/sortedcontainers/sortedlist.py +2646 -0
- sdata/contrib/sortedcontainers/sortedset.py +733 -0
- sdata/contrib/sqlitedict.py +598 -0
- sdata/contrib/timeflake/__init__.py +40 -0
- sdata/contrib/timeflake/flake.py +69 -0
- sdata/contrib/timeflake/utils.py +47 -0
- sdata/deprecated/__init__.py +10 -0
- sdata/deprecated/data.py +1503 -0
- sdata/did/__init__.py +129 -0
- sdata/did/_http.py +67 -0
- sdata/did/base58btc.py +49 -0
- sdata/did/did_github.py +152 -0
- sdata/did/did_peer4.py +155 -0
- sdata/did/did_web.py +222 -0
- sdata/did/diddoc.py +75 -0
- sdata/did/ed25519.py +225 -0
- sdata/did/eddsa.py +107 -0
- sdata/did/errors.py +40 -0
- sdata/did/issue_vc.py +193 -0
- sdata/did/jose.py +126 -0
- sdata/did/keys.py +104 -0
- sdata/did/utils_didkey.py +161 -0
- sdata/did/verify_vp.py +158 -0
- sdata/doe.py +101 -0
- sdata/dtypes.py +510 -0
- sdata/experiments/__init__.py +331 -0
- sdata/experiments/blob0.py +131 -0
- sdata/experiments/file.py +42 -0
- sdata/experiments/filegroup.py +144 -0
- sdata/experiments/image_old.py +313 -0
- sdata/experiments/ks2.py +15 -0
- sdata/experiments/lapshear.py +8 -0
- sdata/experiments/material.py +7 -0
- sdata/experiments/parameters_deprecated.py +394 -0
- sdata/experiments/workbook.py +226 -0
- sdata/format.py +125 -0
- sdata/imagemeta.py +533 -0
- sdata/interactive.py +118 -0
- sdata/iolib/__init__.py +51 -0
- sdata/iolib/hashtree.py +264 -0
- sdata/iolib/json1sqlitestore.py +554 -0
- sdata/iolib/jsonsqlitestore.py +704 -0
- sdata/iolib/pud.py +242 -0
- sdata/iolib/reader.py +262 -0
- sdata/iolib/writer.py +419 -0
- sdata/metadata.py +882 -0
- sdata/parameter.py +414 -0
- sdata/schema.py +273 -0
- sdata/sclass/__init__.py +111 -0
- sdata/sclass/bfo_classes.py +362 -0
- sdata/sclass/blob.py +362 -0
- sdata/sclass/content.py +87 -0
- sdata/sclass/dataframe.py +1251 -0
- sdata/sclass/dataframegroup.py +187 -0
- sdata/sclass/dependency_graph.py +85 -0
- sdata/sclass/filereference.py +124 -0
- sdata/sclass/image.py +218 -0
- sdata/sclass/lazy_namespace.py +155 -0
- sdata/sclass/process.py +383 -0
- sdata/sclass/process_graph.py +221 -0
- sdata/sclass/zipfile.py +135 -0
- sdata/semantic.py +346 -0
- sdata/suuid.py +136 -0
- sdata/timestamp.py +336 -0
- sdata/tools.py +14 -0
- sdata/units.py +602 -0
- sdata/vocab.py +149 -0
- sdata-1.5.0.dist-info/METADATA +264 -0
- sdata-1.5.0.dist-info/RECORD +98 -0
- sdata-1.5.0.dist-info/WHEEL +5 -0
- sdata-1.5.0.dist-info/licenses/LICENSE-MIT +21 -0
- sdata-1.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import struct
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from ._common import *
|
|
6
|
+
from ._exceptions import InvalidImageDataError
|
|
7
|
+
from . import _webp
|
|
8
|
+
|
|
9
|
+
def insert(exif, image, new_file=None):
|
|
10
|
+
"""
|
|
11
|
+
py:function:: piexif.insert(exif_bytes, filename)
|
|
12
|
+
|
|
13
|
+
Insert exif into JPEG.
|
|
14
|
+
|
|
15
|
+
:param bytes exif_bytes: Exif as bytes
|
|
16
|
+
:param str filename: JPEG
|
|
17
|
+
"""
|
|
18
|
+
if exif[0:6] != b"\x45\x78\x69\x66\x00\x00":
|
|
19
|
+
raise ValueError("Given data is not exif data")
|
|
20
|
+
|
|
21
|
+
output_file = False
|
|
22
|
+
# Prevents "UnicodeWarning: Unicode equal comparison failed" warnings on Python 2
|
|
23
|
+
maybe_image = sys.version_info >= (3,0,0) or isinstance(image, str)
|
|
24
|
+
|
|
25
|
+
if maybe_image and image[0:2] == b"\xff\xd8":
|
|
26
|
+
image_data = image
|
|
27
|
+
file_type = "jpeg"
|
|
28
|
+
elif maybe_image and image[0:4] == b"RIFF" and image[8:12] == b"WEBP":
|
|
29
|
+
image_data = image
|
|
30
|
+
file_type = "webp"
|
|
31
|
+
else:
|
|
32
|
+
with open(image, 'rb') as f:
|
|
33
|
+
image_data = f.read()
|
|
34
|
+
if image_data[0:2] == b"\xff\xd8":
|
|
35
|
+
file_type = "jpeg"
|
|
36
|
+
elif image_data[0:4] == b"RIFF" and image_data[8:12] == b"WEBP":
|
|
37
|
+
file_type = "webp"
|
|
38
|
+
else:
|
|
39
|
+
raise InvalidImageDataError
|
|
40
|
+
output_file = True
|
|
41
|
+
|
|
42
|
+
if file_type == "jpeg":
|
|
43
|
+
exif = b"\xff\xe1" + struct.pack(">H", len(exif) + 2) + exif
|
|
44
|
+
segments = split_into_segments(image_data)
|
|
45
|
+
new_data = merge_segments(segments, exif)
|
|
46
|
+
elif file_type == "webp":
|
|
47
|
+
exif = exif[6:]
|
|
48
|
+
new_data = _webp.insert(image_data, exif)
|
|
49
|
+
|
|
50
|
+
if isinstance(new_file, io.BytesIO):
|
|
51
|
+
new_file.write(new_data)
|
|
52
|
+
new_file.seek(0)
|
|
53
|
+
elif new_file:
|
|
54
|
+
with open(new_file, "wb+") as f:
|
|
55
|
+
f.write(new_data)
|
|
56
|
+
elif output_file:
|
|
57
|
+
with open(image, "wb+") as f:
|
|
58
|
+
f.write(new_data)
|
|
59
|
+
else:
|
|
60
|
+
raise ValueError("Give a 3rd argument to 'insert' to output file")
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import struct
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from ._common import *
|
|
5
|
+
from ._exceptions import InvalidImageDataError
|
|
6
|
+
from ._exif import *
|
|
7
|
+
from . import _webp
|
|
8
|
+
|
|
9
|
+
LITTLE_ENDIAN = b"\x49\x49"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load(input_data, key_is_name=False):
|
|
13
|
+
"""
|
|
14
|
+
py:function:: piexif.load(filename)
|
|
15
|
+
|
|
16
|
+
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.
|
|
17
|
+
|
|
18
|
+
:param str filename: JPEG or TIFF
|
|
19
|
+
:return: Exif data({"0th":dict, "Exif":dict, "GPS":dict, "Interop":dict, "1st":dict, "thumbnail":bytes})
|
|
20
|
+
:rtype: dict
|
|
21
|
+
"""
|
|
22
|
+
exif_dict = {"0th":{},
|
|
23
|
+
"Exif":{},
|
|
24
|
+
"GPS":{},
|
|
25
|
+
"Interop":{},
|
|
26
|
+
"1st":{},
|
|
27
|
+
"thumbnail":None}
|
|
28
|
+
exifReader = _ExifReader(input_data)
|
|
29
|
+
if exifReader.tiftag is None:
|
|
30
|
+
return exif_dict
|
|
31
|
+
|
|
32
|
+
if exifReader.tiftag[0:2] == LITTLE_ENDIAN:
|
|
33
|
+
exifReader.endian_mark = "<"
|
|
34
|
+
else:
|
|
35
|
+
exifReader.endian_mark = ">"
|
|
36
|
+
|
|
37
|
+
pointer = struct.unpack(exifReader.endian_mark + "L",
|
|
38
|
+
exifReader.tiftag[4:8])[0]
|
|
39
|
+
exif_dict["0th"] = exifReader.get_ifd_dict(pointer, "0th")
|
|
40
|
+
first_ifd_pointer = exif_dict["0th"].pop("first_ifd_pointer")
|
|
41
|
+
if ImageIFD.ExifTag in exif_dict["0th"]:
|
|
42
|
+
pointer = exif_dict["0th"][ImageIFD.ExifTag]
|
|
43
|
+
exif_dict["Exif"] = exifReader.get_ifd_dict(pointer, "Exif")
|
|
44
|
+
if ImageIFD.GPSTag in exif_dict["0th"]:
|
|
45
|
+
pointer = exif_dict["0th"][ImageIFD.GPSTag]
|
|
46
|
+
exif_dict["GPS"] = exifReader.get_ifd_dict(pointer, "GPS")
|
|
47
|
+
if ExifIFD.InteroperabilityTag in exif_dict["Exif"]:
|
|
48
|
+
pointer = exif_dict["Exif"][ExifIFD.InteroperabilityTag]
|
|
49
|
+
exif_dict["Interop"] = exifReader.get_ifd_dict(pointer, "Interop")
|
|
50
|
+
if first_ifd_pointer != b"\x00\x00\x00\x00":
|
|
51
|
+
pointer = struct.unpack(exifReader.endian_mark + "L",
|
|
52
|
+
first_ifd_pointer)[0]
|
|
53
|
+
exif_dict["1st"] = exifReader.get_ifd_dict(pointer, "1st")
|
|
54
|
+
if (ImageIFD.JPEGInterchangeFormat in exif_dict["1st"] and
|
|
55
|
+
ImageIFD.JPEGInterchangeFormatLength in exif_dict["1st"]):
|
|
56
|
+
end = (exif_dict["1st"][ImageIFD.JPEGInterchangeFormat] +
|
|
57
|
+
exif_dict["1st"][ImageIFD.JPEGInterchangeFormatLength])
|
|
58
|
+
thumb = exifReader.tiftag[exif_dict["1st"][ImageIFD.JPEGInterchangeFormat]:end]
|
|
59
|
+
exif_dict["thumbnail"] = thumb
|
|
60
|
+
|
|
61
|
+
if key_is_name:
|
|
62
|
+
exif_dict = _get_key_name_dict(exif_dict)
|
|
63
|
+
return exif_dict
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class _ExifReader(object):
|
|
67
|
+
def __init__(self, data):
|
|
68
|
+
# Prevents "UnicodeWarning: Unicode equal comparison failed" warnings on Python 2
|
|
69
|
+
maybe_image = sys.version_info >= (3,0,0) or isinstance(data, str)
|
|
70
|
+
|
|
71
|
+
if maybe_image and data[0:2] == b"\xff\xd8": # JPEG
|
|
72
|
+
segments = split_into_segments(data)
|
|
73
|
+
app1 = get_exif_seg(segments)
|
|
74
|
+
if app1:
|
|
75
|
+
self.tiftag = app1[10:]
|
|
76
|
+
else:
|
|
77
|
+
self.tiftag = None
|
|
78
|
+
elif maybe_image and data[0:2] in (b"\x49\x49", b"\x4d\x4d"): # TIFF
|
|
79
|
+
self.tiftag = data
|
|
80
|
+
elif maybe_image and data[0:4] == b"RIFF" and data[8:12] == b"WEBP":
|
|
81
|
+
self.tiftag = _webp.get_exif(data)
|
|
82
|
+
elif maybe_image and data[0:4] == b"Exif": # Exif
|
|
83
|
+
self.tiftag = data[6:]
|
|
84
|
+
else:
|
|
85
|
+
with open(data, 'rb') as f:
|
|
86
|
+
magic_number = f.read(2)
|
|
87
|
+
if magic_number == b"\xff\xd8": # JPEG
|
|
88
|
+
app1 = read_exif_from_file(data)
|
|
89
|
+
if app1:
|
|
90
|
+
self.tiftag = app1[10:]
|
|
91
|
+
else:
|
|
92
|
+
self.tiftag = None
|
|
93
|
+
elif magic_number in (b"\x49\x49", b"\x4d\x4d"): # TIFF
|
|
94
|
+
with open(data, 'rb') as f:
|
|
95
|
+
self.tiftag = f.read()
|
|
96
|
+
else:
|
|
97
|
+
with open(data, 'rb') as f:
|
|
98
|
+
header = f.read(12)
|
|
99
|
+
if header[0:4] == b"RIFF"and header[8:12] == b"WEBP":
|
|
100
|
+
with open(data, 'rb') as f:
|
|
101
|
+
file_data = f.read()
|
|
102
|
+
self.tiftag = _webp.get_exif(file_data)
|
|
103
|
+
else:
|
|
104
|
+
raise InvalidImageDataError("Given file is neither JPEG nor TIFF.")
|
|
105
|
+
|
|
106
|
+
def get_ifd_dict(self, pointer, ifd_name, read_unknown=False):
|
|
107
|
+
ifd_dict = {}
|
|
108
|
+
tag_count = struct.unpack(self.endian_mark + "H",
|
|
109
|
+
self.tiftag[pointer: pointer+2])[0]
|
|
110
|
+
offset = pointer + 2
|
|
111
|
+
if ifd_name in ["0th", "1st"]:
|
|
112
|
+
t = "Image"
|
|
113
|
+
else:
|
|
114
|
+
t = ifd_name
|
|
115
|
+
p_and_value = []
|
|
116
|
+
for x in range(tag_count):
|
|
117
|
+
pointer = offset + 12 * x
|
|
118
|
+
tag = struct.unpack(self.endian_mark + "H",
|
|
119
|
+
self.tiftag[pointer: pointer+2])[0]
|
|
120
|
+
value_type = struct.unpack(self.endian_mark + "H",
|
|
121
|
+
self.tiftag[pointer + 2: pointer + 4])[0]
|
|
122
|
+
value_num = struct.unpack(self.endian_mark + "L",
|
|
123
|
+
self.tiftag[pointer + 4: pointer + 8]
|
|
124
|
+
)[0]
|
|
125
|
+
value = self.tiftag[pointer+8: pointer+12]
|
|
126
|
+
p_and_value.append((pointer, value_type, value_num, value))
|
|
127
|
+
v_set = (value_type, value_num, value, tag)
|
|
128
|
+
if tag in TAGS[t]:
|
|
129
|
+
ifd_dict[tag] = self.convert_value(v_set)
|
|
130
|
+
elif read_unknown:
|
|
131
|
+
ifd_dict[tag] = (v_set[0], v_set[1], v_set[2], self.tiftag)
|
|
132
|
+
#else:
|
|
133
|
+
# pass
|
|
134
|
+
|
|
135
|
+
if ifd_name == "0th":
|
|
136
|
+
pointer = offset + 12 * tag_count
|
|
137
|
+
ifd_dict["first_ifd_pointer"] = self.tiftag[pointer:pointer + 4]
|
|
138
|
+
return ifd_dict
|
|
139
|
+
|
|
140
|
+
def convert_value(self, val):
|
|
141
|
+
data = None
|
|
142
|
+
t = val[0]
|
|
143
|
+
length = val[1]
|
|
144
|
+
value = val[2]
|
|
145
|
+
|
|
146
|
+
if t == TYPES.Byte: # BYTE
|
|
147
|
+
if length > 4:
|
|
148
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
149
|
+
data = struct.unpack("B" * length,
|
|
150
|
+
self.tiftag[pointer: pointer + length])
|
|
151
|
+
else:
|
|
152
|
+
data = struct.unpack("B" * length, value[0:length])
|
|
153
|
+
elif t == TYPES.Ascii: # ASCII
|
|
154
|
+
if length > 4:
|
|
155
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
156
|
+
data = self.tiftag[pointer: pointer+length - 1]
|
|
157
|
+
else:
|
|
158
|
+
data = value[0: length - 1]
|
|
159
|
+
elif t == TYPES.Short: # SHORT
|
|
160
|
+
if length > 2:
|
|
161
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
162
|
+
data = struct.unpack(self.endian_mark + "H" * length,
|
|
163
|
+
self.tiftag[pointer: pointer+length*2])
|
|
164
|
+
else:
|
|
165
|
+
data = struct.unpack(self.endian_mark + "H" * length,
|
|
166
|
+
value[0:length * 2])
|
|
167
|
+
elif t == TYPES.Long: # LONG
|
|
168
|
+
if length > 1:
|
|
169
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
170
|
+
data = struct.unpack(self.endian_mark + "L" * length,
|
|
171
|
+
self.tiftag[pointer: pointer+length*4])
|
|
172
|
+
else:
|
|
173
|
+
data = struct.unpack(self.endian_mark + "L" * length,
|
|
174
|
+
value)
|
|
175
|
+
elif t == TYPES.Rational: # RATIONAL
|
|
176
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
177
|
+
if length > 1:
|
|
178
|
+
data = tuple(
|
|
179
|
+
(struct.unpack(self.endian_mark + "L",
|
|
180
|
+
self.tiftag[pointer + x * 8:
|
|
181
|
+
pointer + 4 + x * 8])[0],
|
|
182
|
+
struct.unpack(self.endian_mark + "L",
|
|
183
|
+
self.tiftag[pointer + 4 + x * 8:
|
|
184
|
+
pointer + 8 + x * 8])[0])
|
|
185
|
+
for x in range(length)
|
|
186
|
+
)
|
|
187
|
+
else:
|
|
188
|
+
data = (struct.unpack(self.endian_mark + "L",
|
|
189
|
+
self.tiftag[pointer: pointer + 4])[0],
|
|
190
|
+
struct.unpack(self.endian_mark + "L",
|
|
191
|
+
self.tiftag[pointer + 4: pointer + 8]
|
|
192
|
+
)[0])
|
|
193
|
+
elif t == TYPES.SByte: # SIGNED BYTES
|
|
194
|
+
if length > 4:
|
|
195
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
196
|
+
data = struct.unpack("b" * length,
|
|
197
|
+
self.tiftag[pointer: pointer + length])
|
|
198
|
+
else:
|
|
199
|
+
data = struct.unpack("b" * length, value[0:length])
|
|
200
|
+
elif t == TYPES.Undefined: # UNDEFINED BYTES
|
|
201
|
+
if length > 4:
|
|
202
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
203
|
+
data = self.tiftag[pointer: pointer+length]
|
|
204
|
+
else:
|
|
205
|
+
data = value[0:length]
|
|
206
|
+
elif t == TYPES.SShort: # SIGNED SHORT
|
|
207
|
+
if length > 2:
|
|
208
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
209
|
+
data = struct.unpack(self.endian_mark + "h" * length,
|
|
210
|
+
self.tiftag[pointer: pointer+length*2])
|
|
211
|
+
else:
|
|
212
|
+
data = struct.unpack(self.endian_mark + "h" * length,
|
|
213
|
+
value[0:length * 2])
|
|
214
|
+
elif t == TYPES.SLong: # SLONG
|
|
215
|
+
if length > 1:
|
|
216
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
217
|
+
data = struct.unpack(self.endian_mark + "l" * length,
|
|
218
|
+
self.tiftag[pointer: pointer+length*4])
|
|
219
|
+
else:
|
|
220
|
+
data = struct.unpack(self.endian_mark + "l" * length,
|
|
221
|
+
value)
|
|
222
|
+
elif t == TYPES.SRational: # SRATIONAL
|
|
223
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
224
|
+
if length > 1:
|
|
225
|
+
data = tuple(
|
|
226
|
+
(struct.unpack(self.endian_mark + "l",
|
|
227
|
+
self.tiftag[pointer + x * 8: pointer + 4 + x * 8])[0],
|
|
228
|
+
struct.unpack(self.endian_mark + "l",
|
|
229
|
+
self.tiftag[pointer + 4 + x * 8: pointer + 8 + x * 8])[0])
|
|
230
|
+
for x in range(length)
|
|
231
|
+
)
|
|
232
|
+
else:
|
|
233
|
+
data = (struct.unpack(self.endian_mark + "l",
|
|
234
|
+
self.tiftag[pointer: pointer + 4])[0],
|
|
235
|
+
struct.unpack(self.endian_mark + "l",
|
|
236
|
+
self.tiftag[pointer + 4: pointer + 8]
|
|
237
|
+
)[0])
|
|
238
|
+
elif t == TYPES.Float: # FLOAT
|
|
239
|
+
if length > 1:
|
|
240
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
241
|
+
data = struct.unpack(self.endian_mark + "f" * length,
|
|
242
|
+
self.tiftag[pointer: pointer+length*4])
|
|
243
|
+
else:
|
|
244
|
+
data = struct.unpack(self.endian_mark + "f" * length,
|
|
245
|
+
value)
|
|
246
|
+
elif t == TYPES.DFloat: # DOUBLE
|
|
247
|
+
pointer = struct.unpack(self.endian_mark + "L", value)[0]
|
|
248
|
+
data = struct.unpack(self.endian_mark + "d" * length,
|
|
249
|
+
self.tiftag[pointer: pointer+length*8])
|
|
250
|
+
else:
|
|
251
|
+
raise ValueError("Exif might be wrong. Got incorrect value " +
|
|
252
|
+
"type to decode.\n" +
|
|
253
|
+
"tag: " + str(val[3]) + "\ntype: " + str(t))
|
|
254
|
+
|
|
255
|
+
if isinstance(data, tuple) and (len(data) == 1):
|
|
256
|
+
return data[0]
|
|
257
|
+
else:
|
|
258
|
+
return data
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _get_key_name_dict(exif_dict):
|
|
262
|
+
new_dict = {
|
|
263
|
+
"0th":{TAGS["Image"][n]["name"]:value for n, value in exif_dict["0th"].items()},
|
|
264
|
+
"Exif":{TAGS["Exif"][n]["name"]:value for n, value in exif_dict["Exif"].items()},
|
|
265
|
+
"1st":{TAGS["Image"][n]["name"]:value for n, value in exif_dict["1st"].items()},
|
|
266
|
+
"GPS":{TAGS["GPS"][n]["name"]:value for n, value in exif_dict["GPS"].items()},
|
|
267
|
+
"Interop":{TAGS["Interop"][n]["name"]:value for n, value in exif_dict["Interop"].items()},
|
|
268
|
+
"thumbnail":exif_dict["thumbnail"],
|
|
269
|
+
}
|
|
270
|
+
return new_dict
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
from ._common import *
|
|
4
|
+
from . import _webp
|
|
5
|
+
|
|
6
|
+
def remove(src, new_file=None):
|
|
7
|
+
"""
|
|
8
|
+
py:function:: piexif.remove(filename)
|
|
9
|
+
|
|
10
|
+
Remove exif from JPEG.
|
|
11
|
+
|
|
12
|
+
:param str filename: JPEG
|
|
13
|
+
"""
|
|
14
|
+
output_is_file = False
|
|
15
|
+
if src[0:2] == b"\xff\xd8":
|
|
16
|
+
src_data = src
|
|
17
|
+
file_type = "jpeg"
|
|
18
|
+
elif src[0:4] == b"RIFF" and src[8:12] == b"WEBP":
|
|
19
|
+
src_data = src
|
|
20
|
+
file_type = "webp"
|
|
21
|
+
else:
|
|
22
|
+
with open(src, 'rb') as f:
|
|
23
|
+
src_data = f.read()
|
|
24
|
+
output_is_file = True
|
|
25
|
+
if src_data[0:2] == b"\xff\xd8":
|
|
26
|
+
file_type = "jpeg"
|
|
27
|
+
elif src_data[0:4] == b"RIFF" and src_data[8:12] == b"WEBP":
|
|
28
|
+
file_type = "webp"
|
|
29
|
+
|
|
30
|
+
if file_type == "jpeg":
|
|
31
|
+
segments = split_into_segments(src_data)
|
|
32
|
+
exif = get_exif_seg(segments)
|
|
33
|
+
if exif:
|
|
34
|
+
new_data = src_data.replace(exif, b"")
|
|
35
|
+
else:
|
|
36
|
+
new_data = src_data
|
|
37
|
+
elif file_type == "webp":
|
|
38
|
+
try:
|
|
39
|
+
new_data = _webp.remove(src_data)
|
|
40
|
+
except ValueError:
|
|
41
|
+
new_data = src_data
|
|
42
|
+
except e:
|
|
43
|
+
print(e.args)
|
|
44
|
+
raise ValueError("Error occurred.")
|
|
45
|
+
|
|
46
|
+
if isinstance(new_file, io.BytesIO):
|
|
47
|
+
new_file.write(new_data)
|
|
48
|
+
new_file.seek(0)
|
|
49
|
+
elif new_file:
|
|
50
|
+
with open(new_file, "wb+") as f:
|
|
51
|
+
f.write(new_data)
|
|
52
|
+
elif output_is_file:
|
|
53
|
+
with open(src, "wb+") as f:
|
|
54
|
+
f.write(new_data)
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError("Give a second argument to 'remove' to output file")
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
from ._common import *
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def transplant(exif_src, image, new_file=None):
|
|
7
|
+
"""
|
|
8
|
+
py:function:: piexif.transplant(filename1, filename2)
|
|
9
|
+
|
|
10
|
+
Transplant exif from filename1 to filename2.
|
|
11
|
+
|
|
12
|
+
:param str filename1: JPEG
|
|
13
|
+
:param str filename2: JPEG
|
|
14
|
+
"""
|
|
15
|
+
if exif_src[0:2] == b"\xff\xd8":
|
|
16
|
+
src_data = exif_src
|
|
17
|
+
else:
|
|
18
|
+
with open(exif_src, 'rb') as f:
|
|
19
|
+
src_data = f.read()
|
|
20
|
+
segments = split_into_segments(src_data)
|
|
21
|
+
exif = get_exif_seg(segments)
|
|
22
|
+
if exif is None:
|
|
23
|
+
raise ValueError("not found exif in input")
|
|
24
|
+
|
|
25
|
+
output_file = False
|
|
26
|
+
if image[0:2] == b"\xff\xd8":
|
|
27
|
+
image_data = image
|
|
28
|
+
else:
|
|
29
|
+
with open(image, 'rb') as f:
|
|
30
|
+
image_data = f.read()
|
|
31
|
+
output_file = True
|
|
32
|
+
segments = split_into_segments(image_data)
|
|
33
|
+
new_data = merge_segments(segments, exif)
|
|
34
|
+
|
|
35
|
+
if isinstance(new_file, io.BytesIO):
|
|
36
|
+
new_file.write(new_data)
|
|
37
|
+
new_file.seek(0)
|
|
38
|
+
elif new_file:
|
|
39
|
+
with open(new_file, "wb+") as f:
|
|
40
|
+
f.write(new_data)
|
|
41
|
+
elif output_file:
|
|
42
|
+
with open(image, "wb+") as f:
|
|
43
|
+
f.write(new_data)
|
|
44
|
+
else:
|
|
45
|
+
raise ValueError("Give a 3rd argument to 'transplant' to output file")
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
|
|
2
|
+
import struct
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def split(data):
|
|
6
|
+
if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
|
|
7
|
+
raise ValueError("Not WebP")
|
|
8
|
+
|
|
9
|
+
webp_length_bytes = data[4:8]
|
|
10
|
+
webp_length = struct.unpack("<L", webp_length_bytes)[0]
|
|
11
|
+
RIFF_HEADER_SIZE = 8
|
|
12
|
+
file_size = RIFF_HEADER_SIZE + webp_length
|
|
13
|
+
|
|
14
|
+
start = 12
|
|
15
|
+
pointer = start
|
|
16
|
+
CHUNK_FOURCC_LENGTH = 4
|
|
17
|
+
LENGTH_BYTES_LENGTH = 4
|
|
18
|
+
|
|
19
|
+
chunks = []
|
|
20
|
+
while pointer + CHUNK_FOURCC_LENGTH + LENGTH_BYTES_LENGTH < file_size:
|
|
21
|
+
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
|
|
22
|
+
pointer += CHUNK_FOURCC_LENGTH
|
|
23
|
+
chunk_length_bytes = data[pointer:pointer + LENGTH_BYTES_LENGTH]
|
|
24
|
+
chunk_length = struct.unpack("<L", chunk_length_bytes)[0]
|
|
25
|
+
pointer += LENGTH_BYTES_LENGTH
|
|
26
|
+
|
|
27
|
+
chunk_data = data[pointer:pointer + chunk_length]
|
|
28
|
+
chunks.append({"fourcc":fourcc, "length_bytes":chunk_length_bytes, "data":chunk_data})
|
|
29
|
+
|
|
30
|
+
padding = 1 if chunk_length % 2 else 0
|
|
31
|
+
|
|
32
|
+
pointer += chunk_length + padding
|
|
33
|
+
return chunks
|
|
34
|
+
|
|
35
|
+
def merge_chunks(chunks):
|
|
36
|
+
merged = b"".join([chunk["fourcc"]
|
|
37
|
+
+ chunk["length_bytes"]
|
|
38
|
+
+ chunk["data"]
|
|
39
|
+
+ (len(chunk["data"]) % 2) * b"\x00"
|
|
40
|
+
for chunk in chunks])
|
|
41
|
+
return merged
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _get_size_from_vp8x(chunk):
|
|
45
|
+
width_minus_one_bytes = chunk["data"][-6:-3] + b"\x00"
|
|
46
|
+
width_minus_one = struct.unpack("<L", width_minus_one_bytes)[0]
|
|
47
|
+
width = width_minus_one + 1
|
|
48
|
+
height_minus_one_bytes = chunk["data"][-3:] + b"\x00"
|
|
49
|
+
height_minus_one = struct.unpack("<L", height_minus_one_bytes)[0]
|
|
50
|
+
height = height_minus_one + 1
|
|
51
|
+
return (width, height)
|
|
52
|
+
|
|
53
|
+
def _get_size_from_vp8(chunk):
|
|
54
|
+
BEGIN_CODE = b"\x9d\x01\x2a"
|
|
55
|
+
begin_index = chunk["data"].find(BEGIN_CODE)
|
|
56
|
+
if begin_index == -1:
|
|
57
|
+
ValueError("wrong VP8")
|
|
58
|
+
else:
|
|
59
|
+
BEGIN_CODE_LENGTH = len(BEGIN_CODE)
|
|
60
|
+
LENGTH_BYTES_LENGTH = 2
|
|
61
|
+
length_start = begin_index + BEGIN_CODE_LENGTH
|
|
62
|
+
width_bytes = chunk["data"][length_start:length_start + LENGTH_BYTES_LENGTH]
|
|
63
|
+
width = struct.unpack("<H", width_bytes)[0]
|
|
64
|
+
height_bytes = chunk["data"][length_start + LENGTH_BYTES_LENGTH:length_start + 2 * LENGTH_BYTES_LENGTH]
|
|
65
|
+
height = struct.unpack("<H", height_bytes)[0]
|
|
66
|
+
return (width, height)
|
|
67
|
+
|
|
68
|
+
def _vp8L_contains_alpha(chunk_data):
|
|
69
|
+
flag = ord(chunk_data[4:5]) >> 5-1 & ord(b"\x01")
|
|
70
|
+
contains = 1 * flag
|
|
71
|
+
return contains
|
|
72
|
+
|
|
73
|
+
def _get_size_from_vp8L(chunk):
|
|
74
|
+
b1 = chunk["data"][1:2]
|
|
75
|
+
b2 = chunk["data"][2:3]
|
|
76
|
+
b3 = chunk["data"][3:4]
|
|
77
|
+
b4 = chunk["data"][4:5]
|
|
78
|
+
|
|
79
|
+
width_minus_one = (ord(b2) & ord(b"\x3F")) << 8 | ord(b1)
|
|
80
|
+
width = width_minus_one + 1
|
|
81
|
+
|
|
82
|
+
height_minus_one = (ord(b4) & ord(b"\x0F")) << 10 | ord(b3) << 2 | (ord(b2) & ord(b"\xC0")) >> 6
|
|
83
|
+
height = height_minus_one + 1
|
|
84
|
+
|
|
85
|
+
return (width, height)
|
|
86
|
+
|
|
87
|
+
def _get_size_from_anmf(chunk):
|
|
88
|
+
width_minus_one_bytes = chunk["data"][6:9] + b"\x00"
|
|
89
|
+
width_minus_one = struct.unpack("<L", width_minus_one_bytes)[0]
|
|
90
|
+
width = width_minus_one + 1
|
|
91
|
+
height_minus_one_bytes = chunk["data"][9:12] + b"\x00"
|
|
92
|
+
height_minus_one = struct.unpack("<L", height_minus_one_bytes)[0]
|
|
93
|
+
height = height_minus_one + 1
|
|
94
|
+
return (width, height)
|
|
95
|
+
|
|
96
|
+
def set_vp8x(chunks):
|
|
97
|
+
|
|
98
|
+
width = None
|
|
99
|
+
height = None
|
|
100
|
+
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]
|
|
101
|
+
|
|
102
|
+
for chunk in chunks:
|
|
103
|
+
if chunk["fourcc"] == b"VP8X":
|
|
104
|
+
width, height = _get_size_from_vp8x(chunk)
|
|
105
|
+
elif chunk["fourcc"] == b"VP8 ":
|
|
106
|
+
width, height = _get_size_from_vp8(chunk)
|
|
107
|
+
elif chunk["fourcc"] == b"VP8L":
|
|
108
|
+
is_rgba = _vp8L_contains_alpha(chunk["data"])
|
|
109
|
+
if is_rgba:
|
|
110
|
+
flags[3] = b"1"
|
|
111
|
+
width, height = _get_size_from_vp8L(chunk)
|
|
112
|
+
elif chunk["fourcc"] == b"ANMF":
|
|
113
|
+
width, height = _get_size_from_anmf(chunk)
|
|
114
|
+
elif chunk["fourcc"] == b"ICCP":
|
|
115
|
+
flags[2] = b"1"
|
|
116
|
+
elif chunk["fourcc"] == b"ALPH":
|
|
117
|
+
flags[3] = b"1"
|
|
118
|
+
elif chunk["fourcc"] == b"EXIF":
|
|
119
|
+
flags[4] = b"1"
|
|
120
|
+
elif chunk["fourcc"] == b"XMP ":
|
|
121
|
+
flags[5] = b"1"
|
|
122
|
+
elif chunk["fourcc"] == b"ANIM":
|
|
123
|
+
flags[6] = b"1"
|
|
124
|
+
width_minus_one = width - 1
|
|
125
|
+
height_minus_one = height - 1
|
|
126
|
+
|
|
127
|
+
if chunks[0]["fourcc"] == b"VP8X":
|
|
128
|
+
chunks.pop(0)
|
|
129
|
+
|
|
130
|
+
header_bytes = b"VP8X"
|
|
131
|
+
length_bytes = b"\x0a\x00\x00\x00"
|
|
132
|
+
flags_bytes = struct.pack("B", int(b"".join(flags), 2))
|
|
133
|
+
padding_bytes = b"\x00\x00\x00"
|
|
134
|
+
width_bytes = struct.pack("<L", width_minus_one)[:3]
|
|
135
|
+
height_bytes = struct.pack("<L", height_minus_one)[:3]
|
|
136
|
+
|
|
137
|
+
data_bytes = flags_bytes + padding_bytes + width_bytes + height_bytes
|
|
138
|
+
|
|
139
|
+
vp8x_chunk = {"fourcc":header_bytes, "length_bytes":length_bytes, "data":data_bytes}
|
|
140
|
+
chunks.insert(0, vp8x_chunk)
|
|
141
|
+
|
|
142
|
+
return chunks
|
|
143
|
+
|
|
144
|
+
def get_file_header(chunks):
|
|
145
|
+
WEBP_HEADER_LENGTH = 4
|
|
146
|
+
FOURCC_LENGTH = 4
|
|
147
|
+
LENGTH_BYTES_LENGTH = 4
|
|
148
|
+
|
|
149
|
+
length = WEBP_HEADER_LENGTH
|
|
150
|
+
for chunk in chunks:
|
|
151
|
+
data_length = struct.unpack("<L", chunk["length_bytes"])[0]
|
|
152
|
+
data_length += 1 if data_length % 2 else 0
|
|
153
|
+
length += FOURCC_LENGTH + LENGTH_BYTES_LENGTH + data_length
|
|
154
|
+
length_bytes = struct.pack("<L", length)
|
|
155
|
+
riff = b"RIFF"
|
|
156
|
+
webp_header = b"WEBP"
|
|
157
|
+
file_header = riff + length_bytes + webp_header
|
|
158
|
+
return file_header
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_exif(data):
|
|
163
|
+
if data[0:4] != b"RIFF" or data[8:12] != b"WEBP":
|
|
164
|
+
raise ValueError("Not WebP")
|
|
165
|
+
|
|
166
|
+
if data[12:16] != b"VP8X":
|
|
167
|
+
raise ValueError("doesnot have exif")
|
|
168
|
+
|
|
169
|
+
webp_length_bytes = data[4:8]
|
|
170
|
+
webp_length = struct.unpack("<L", webp_length_bytes)[0]
|
|
171
|
+
RIFF_HEADER_SIZE = 8
|
|
172
|
+
file_size = RIFF_HEADER_SIZE + webp_length
|
|
173
|
+
|
|
174
|
+
start = 12
|
|
175
|
+
pointer = start
|
|
176
|
+
CHUNK_FOURCC_LENGTH = 4
|
|
177
|
+
LENGTH_BYTES_LENGTH = 4
|
|
178
|
+
|
|
179
|
+
chunks = []
|
|
180
|
+
exif = b""
|
|
181
|
+
while pointer < file_size:
|
|
182
|
+
fourcc = data[pointer:pointer + CHUNK_FOURCC_LENGTH]
|
|
183
|
+
pointer += CHUNK_FOURCC_LENGTH
|
|
184
|
+
chunk_length_bytes = data[pointer:pointer + LENGTH_BYTES_LENGTH]
|
|
185
|
+
chunk_length = struct.unpack("<L", chunk_length_bytes)[0]
|
|
186
|
+
if chunk_length % 2:
|
|
187
|
+
chunk_length += 1
|
|
188
|
+
pointer += LENGTH_BYTES_LENGTH
|
|
189
|
+
if fourcc == b"EXIF":
|
|
190
|
+
return data[pointer:pointer + chunk_length]
|
|
191
|
+
pointer += chunk_length
|
|
192
|
+
return None # if there isn't exif, return None.
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def insert_exif_into_chunks(chunks, exif_bytes):
|
|
196
|
+
EXIF_HEADER = b"EXIF"
|
|
197
|
+
exif_length_bytes = struct.pack("<L", len(exif_bytes))
|
|
198
|
+
exif_chunk = {"fourcc":EXIF_HEADER, "length_bytes":exif_length_bytes, "data":exif_bytes}
|
|
199
|
+
|
|
200
|
+
xmp_index = None
|
|
201
|
+
animation_index = None
|
|
202
|
+
|
|
203
|
+
for index, chunk in enumerate(chunks):
|
|
204
|
+
if chunk["fourcc"] == b"EXIF":
|
|
205
|
+
chunks.pop(index)
|
|
206
|
+
|
|
207
|
+
for index, chunk in enumerate(chunks):
|
|
208
|
+
if chunk["fourcc"] == b"XMP ":
|
|
209
|
+
xmp_index = index
|
|
210
|
+
elif chunk["fourcc"] == b"ANIM":
|
|
211
|
+
animation_index = index
|
|
212
|
+
if xmp_index is not None:
|
|
213
|
+
chunks.insert(xmp_index, exif_chunk)
|
|
214
|
+
elif animation_index is not None:
|
|
215
|
+
chunks.insert(animation_index, exif_chunk)
|
|
216
|
+
else:
|
|
217
|
+
chunks.append(exif_chunk)
|
|
218
|
+
return chunks
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def insert(webp_bytes, exif_bytes):
|
|
222
|
+
chunks = split(webp_bytes)
|
|
223
|
+
chunks = insert_exif_into_chunks(chunks, exif_bytes)
|
|
224
|
+
chunks = set_vp8x(chunks)
|
|
225
|
+
file_header = get_file_header(chunks)
|
|
226
|
+
merged = merge_chunks(chunks)
|
|
227
|
+
new_webp_bytes = file_header + merged
|
|
228
|
+
return new_webp_bytes
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def remove(webp_bytes):
|
|
232
|
+
chunks = split(webp_bytes)
|
|
233
|
+
for index, chunk in enumerate(chunks):
|
|
234
|
+
if chunk["fourcc"] == b"EXIF":
|
|
235
|
+
chunks.pop(index)
|
|
236
|
+
chunks = set_vp8x(chunks)
|
|
237
|
+
file_header = get_file_header(chunks)
|
|
238
|
+
merged = merge_chunks(chunks)
|
|
239
|
+
new_webp_bytes = file_header + merged
|
|
240
|
+
return new_webp_bytes
|