mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-09-21 03:18:57 +03:00
Merge pull request #3513 from radarhere/custom_tags
Added custom string TIFF tags
This commit is contained in:
commit
7351363fe2
|
@ -235,7 +235,10 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_custom_metadata(self):
|
def test_custom_metadata(self):
|
||||||
custom = {
|
custom = {
|
||||||
37000: 4,
|
37000: 4,
|
||||||
37001: 4.2
|
37001: 4.2,
|
||||||
|
37002: 'custom tag value',
|
||||||
|
37003: u'custom tag value',
|
||||||
|
37004: b'custom tag value'
|
||||||
}
|
}
|
||||||
|
|
||||||
libtiff_version = TiffImagePlugin._libtiff_version()
|
libtiff_version = TiffImagePlugin._libtiff_version()
|
||||||
|
@ -256,6 +259,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
reloaded = Image.open(out)
|
||||||
for tag, value in custom.items():
|
for tag, value in custom.items():
|
||||||
|
if libtiff and isinstance(value, bytes):
|
||||||
|
value = value.decode()
|
||||||
self.assertEqual(reloaded.tag_v2[tag], value)
|
self.assertEqual(reloaded.tag_v2[tag], value)
|
||||||
|
|
||||||
def test_int_dpi(self):
|
def test_int_dpi(self):
|
||||||
|
|
|
@ -557,26 +557,26 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
else:
|
else:
|
||||||
self.tagtype[tag] = 7
|
self.tagtype[tag] = 7
|
||||||
if all(isinstance(v, IFDRational) for v in values):
|
if all(isinstance(v, IFDRational) for v in values):
|
||||||
self.tagtype[tag] = 5
|
self.tagtype[tag] = TiffTags.RATIONAL
|
||||||
elif all(isinstance(v, int) for v in values):
|
elif all(isinstance(v, int) for v in values):
|
||||||
if all(v < 2 ** 16 for v in values):
|
if all(v < 2 ** 16 for v in values):
|
||||||
self.tagtype[tag] = 3
|
self.tagtype[tag] = TiffTags.SHORT
|
||||||
else:
|
else:
|
||||||
self.tagtype[tag] = 4
|
self.tagtype[tag] = TiffTags.LONG
|
||||||
elif all(isinstance(v, float) for v in values):
|
elif all(isinstance(v, float) for v in values):
|
||||||
self.tagtype[tag] = 12
|
self.tagtype[tag] = TiffTags.DOUBLE
|
||||||
else:
|
else:
|
||||||
if py3:
|
if py3:
|
||||||
if all(isinstance(v, str) for v in values):
|
if all(isinstance(v, str) for v in values):
|
||||||
self.tagtype[tag] = 2
|
self.tagtype[tag] = TiffTags.ASCII
|
||||||
else:
|
else:
|
||||||
# Never treat data as binary by default on Python 2.
|
# Never treat data as binary by default on Python 2.
|
||||||
self.tagtype[tag] = 2
|
self.tagtype[tag] = TiffTags.ASCII
|
||||||
|
|
||||||
if self.tagtype[tag] == 7 and py3:
|
if self.tagtype[tag] == TiffTags.UNDEFINED and py3:
|
||||||
values = [value.encode("ascii", 'replace') if isinstance(
|
values = [value.encode("ascii", 'replace') if isinstance(
|
||||||
value, str) else value]
|
value, str) else value]
|
||||||
elif self.tagtype[tag] == 5:
|
elif self.tagtype[tag] == TiffTags.RATIONAL:
|
||||||
values = [float(v) if isinstance(v, int) else v
|
values = [float(v) if isinstance(v, int) else v
|
||||||
for v in values]
|
for v in values]
|
||||||
|
|
||||||
|
@ -592,7 +592,10 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
if (info.length == 1) or \
|
if (info.length == 1) or \
|
||||||
(info.length is None and len(values) == 1 and not legacy_api):
|
(info.length is None and len(values) == 1 and not legacy_api):
|
||||||
# Don't mess with the legacy api, since it's frozen.
|
# Don't mess with the legacy api, since it's frozen.
|
||||||
if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
|
if legacy_api and self.tagtype[tag] in [
|
||||||
|
TiffTags.RATIONAL,
|
||||||
|
TiffTags.SIGNED_RATIONAL
|
||||||
|
]: # rationals
|
||||||
values = values,
|
values = values,
|
||||||
try:
|
try:
|
||||||
dest[tag], = values
|
dest[tag], = values
|
||||||
|
@ -649,13 +652,13 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
b"".join(self._pack(fmt, value) for value in values))
|
b"".join(self._pack(fmt, value) for value in values))
|
||||||
|
|
||||||
list(map(_register_basic,
|
list(map(_register_basic,
|
||||||
[(3, "H", "short"),
|
[(TiffTags.SHORT, "H", "short"),
|
||||||
(4, "L", "long"),
|
(TiffTags.LONG, "L", "long"),
|
||||||
(6, "b", "signed byte"),
|
(TiffTags.SIGNED_BYTE, "b", "signed byte"),
|
||||||
(8, "h", "signed short"),
|
(TiffTags.SIGNED_SHORT, "h", "signed short"),
|
||||||
(9, "l", "signed long"),
|
(TiffTags.SIGNED_LONG, "l", "signed long"),
|
||||||
(11, "f", "float"),
|
(TiffTags.FLOAT, "f", "float"),
|
||||||
(12, "d", "double")]))
|
(TiffTags.DOUBLE, "d", "double")]))
|
||||||
|
|
||||||
@_register_loader(1, 1) # Basic type, except for the legacy API.
|
@_register_loader(1, 1) # Basic type, except for the legacy API.
|
||||||
def load_byte(self, data, legacy_api=True):
|
def load_byte(self, data, legacy_api=True):
|
||||||
|
@ -811,7 +814,10 @@ class ImageFileDirectory_v2(MutableMapping):
|
||||||
print("- value:", values)
|
print("- value:", values)
|
||||||
|
|
||||||
# count is sum of lengths for string and arbitrary data
|
# count is sum of lengths for string and arbitrary data
|
||||||
count = len(data) if typ in [2, 7] else len(values)
|
if typ in [TiffTags.ASCII, TiffTags.UNDEFINED]:
|
||||||
|
count = len(data)
|
||||||
|
else:
|
||||||
|
count = len(values)
|
||||||
# figure out if data fits into the entry
|
# figure out if data fits into the entry
|
||||||
if len(data) <= 4:
|
if len(data) <= 4:
|
||||||
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
|
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
|
||||||
|
@ -1513,12 +1519,16 @@ def _save(im, fp, filename):
|
||||||
getattr(im, 'tag_v2', {}).items(),
|
getattr(im, 'tag_v2', {}).items(),
|
||||||
legacy_ifd.items()):
|
legacy_ifd.items()):
|
||||||
# Libtiff can only process certain core items without adding
|
# Libtiff can only process certain core items without adding
|
||||||
# them to the custom dictionary. Support has only been been added
|
# them to the custom dictionary.
|
||||||
# for int and float values
|
# Support for custom items has only been been added
|
||||||
|
# for int, float, unicode, string and byte values
|
||||||
if tag not in TiffTags.LIBTIFF_CORE:
|
if tag not in TiffTags.LIBTIFF_CORE:
|
||||||
|
if TiffTags.lookup(tag).type == TiffTags.UNDEFINED:
|
||||||
|
continue
|
||||||
if (distutils.version.StrictVersion(_libtiff_version()) <
|
if (distutils.version.StrictVersion(_libtiff_version()) <
|
||||||
distutils.version.StrictVersion("4.0")) \
|
distutils.version.StrictVersion("4.0")) \
|
||||||
or not (isinstance(value, int) or isinstance(value, float)):
|
or not (isinstance(value, (int, float, str, bytes)) or
|
||||||
|
(not py3 and isinstance(value, unicode))): # noqa: F821
|
||||||
continue
|
continue
|
||||||
if tag not in atts and tag not in blocklist:
|
if tag not in atts and tag not in blocklist:
|
||||||
if isinstance(value, str if py3 else unicode): # noqa: F821
|
if isinstance(value, str if py3 else unicode): # noqa: F821
|
||||||
|
@ -1799,7 +1809,7 @@ class AppendingTiffWriter:
|
||||||
# local (not referenced with another offset)
|
# local (not referenced with another offset)
|
||||||
self.rewriteLastShortToLong(offset)
|
self.rewriteLastShortToLong(offset)
|
||||||
self.f.seek(-10, os.SEEK_CUR)
|
self.f.seek(-10, os.SEEK_CUR)
|
||||||
self.writeShort(4) # rewrite the type to LONG
|
self.writeShort(TiffTags.LONG) # rewrite the type to LONG
|
||||||
self.f.seek(8, os.SEEK_CUR)
|
self.f.seek(8, os.SEEK_CUR)
|
||||||
elif isShort:
|
elif isShort:
|
||||||
self.rewriteLastShort(offset)
|
self.rewriteLastShort(offset)
|
||||||
|
|
|
@ -64,8 +64,12 @@ ASCII = 2
|
||||||
SHORT = 3
|
SHORT = 3
|
||||||
LONG = 4
|
LONG = 4
|
||||||
RATIONAL = 5
|
RATIONAL = 5
|
||||||
|
SIGNED_BYTE = 6
|
||||||
UNDEFINED = 7
|
UNDEFINED = 7
|
||||||
|
SIGNED_SHORT = 8
|
||||||
|
SIGNED_LONG = 9
|
||||||
SIGNED_RATIONAL = 10
|
SIGNED_RATIONAL = 10
|
||||||
|
FLOAT = 11
|
||||||
DOUBLE = 12
|
DOUBLE = 12
|
||||||
|
|
||||||
TAGS_V2 = {
|
TAGS_V2 = {
|
||||||
|
|
|
@ -885,9 +885,11 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
|
||||||
}
|
}
|
||||||
} else if (PyBytes_Check(value)) {
|
} else if (PyBytes_Check(value)) {
|
||||||
TRACE(("Setting from Bytes: %d, %s \n", key_int, PyBytes_AsString(value)));
|
TRACE(("Setting from Bytes: %d, %s \n", key_int, PyBytes_AsString(value)));
|
||||||
|
if (is_core_tag || !ImagingLibTiffMergeFieldInfo(&encoder->state, TIFF_ASCII, key_int)) {
|
||||||
status = ImagingLibTiffSetField(&encoder->state,
|
status = ImagingLibTiffSetField(&encoder->state,
|
||||||
(ttag_t) PyInt_AsLong(key),
|
(ttag_t) PyInt_AsLong(key),
|
||||||
PyBytes_AsString(value));
|
PyBytes_AsString(value));
|
||||||
|
}
|
||||||
} else if (PyTuple_Check(value)) {
|
} else if (PyTuple_Check(value)) {
|
||||||
Py_ssize_t len,i;
|
Py_ssize_t len,i;
|
||||||
float *floatav;
|
float *floatav;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user