Skip to content

Commit

Permalink
Merge pull request #1988 from wiredfool/iccprofile
Browse files Browse the repository at this point in the history
Binary Tiff Metadata/ICC profile.
  • Loading branch information
wiredfool committed Jun 29, 2016
2 parents 7198c85 + d151b03 commit ffcc067
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 22 deletions.
6 changes: 2 additions & 4 deletions PIL/TiffImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,7 @@ def _setitem(self, tag, value, legacy_api):
self.tagtype[tag] = 2

if self.tagtype[tag] == 7 and bytes is not str:
values = [value.encode("ascii", 'replace') if isinstance(value, str) else value
for value in values]
values = [value.encode("ascii", 'replace') if isinstance(value, str) else value]

values = tuple(info.cvt_enum(value) for value in values)

Expand Down Expand Up @@ -604,8 +603,7 @@ def _register_basic(idx_fmt_name):

@_register_loader(1, 1) # Basic type, except for the legacy API.
def load_byte(self, data, legacy_api=True):
return (data if legacy_api else
tuple(map(ord, data) if bytes is str else data))
return data

@_register_writer(1) # Basic type, except for the legacy API.
def write_byte(self, data):
Expand Down
42 changes: 26 additions & 16 deletions PIL/TiffTags.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,21 @@ def lookup(tag):
#
# id: (Name, Type, Length, enum_values)
#
# The length here differs from the length in the tiff spec. For
# numbers, the tiff spec is for the number of fields returned. We
# agree here. For string-like types, the tiff spec uses the length of
# field in bytes. In Pillow, we are using the number of expected
# fields, in general 1 for string-like types.


BYTE = 1
ASCII = 2
SHORT = 3
LONG = 4
RATIONAL = 5
UNDEFINED = 7
SIGNED_RATIONAL = 10
DOUBLE = 12

TAGS_V2 = {

Expand Down Expand Up @@ -128,8 +138,8 @@ def lookup(tag):
338: ("ExtraSamples", SHORT, 0),
339: ("SampleFormat", SHORT, 0),

340: ("SMinSampleValue", 12, 0),
341: ("SMaxSampleValue", 12, 0),
340: ("SMinSampleValue", DOUBLE, 0),
341: ("SMaxSampleValue", DOUBLE, 0),
342: ("TransferRange", SHORT, 6),

# obsolete JPEG tags
Expand All @@ -152,34 +162,34 @@ def lookup(tag):

# FIXME add more tags here
34665: ("ExifIFD", SHORT, 1),
34675: ('ICCProfile', 7, 0),
34853: ('GPSInfoIFD', 1, 1),
34675: ('ICCProfile', UNDEFINED, 1),
34853: ('GPSInfoIFD', BYTE, 1),

# MPInfo
45056: ("MPFVersion", 7, 1),
45056: ("MPFVersion", UNDEFINED, 1),
45057: ("NumberOfImages", LONG, 1),
45058: ("MPEntry", 7, 1),
45059: ("ImageUIDList", 7, 0),
45058: ("MPEntry", UNDEFINED, 1),
45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check
45060: ("TotalFrames", LONG, 1),
45313: ("MPIndividualNum", LONG, 1),
45569: ("PanOrientation", LONG, 1),
45570: ("PanOverlap_H", RATIONAL, 1),
45571: ("PanOverlap_V", RATIONAL, 1),
45572: ("BaseViewpointNum", LONG, 1),
45573: ("ConvergenceAngle", 10, 1),
45573: ("ConvergenceAngle", SIGNED_RATIONAL, 1),
45574: ("BaselineLength", RATIONAL, 1),
45575: ("VerticalDivergence", 10, 1),
45576: ("AxisDistance_X", 10, 1),
45577: ("AxisDistance_Y", 10, 1),
45578: ("AxisDistance_Z", 10, 1),
45579: ("YawAngle", 10, 1),
45580: ("PitchAngle", 10, 1),
45581: ("RollAngle", 10, 1),
45575: ("VerticalDivergence", SIGNED_RATIONAL, 1),
45576: ("AxisDistance_X", SIGNED_RATIONAL, 1),
45577: ("AxisDistance_Y", SIGNED_RATIONAL, 1),
45578: ("AxisDistance_Z", SIGNED_RATIONAL, 1),
45579: ("YawAngle", SIGNED_RATIONAL, 1),
45580: ("PitchAngle", SIGNED_RATIONAL, 1),
45581: ("RollAngle", SIGNED_RATIONAL, 1),

50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}),
50780: ("BestQualityScale", RATIONAL, 1),
50838: ("ImageJMetaDataByteCounts", LONG, 1),
50839: ("ImageJMetaData", 7, 1)
50839: ("ImageJMetaData", UNDEFINED, 1)
}

# Legacy Tags structure
Expand Down
2 changes: 1 addition & 1 deletion Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ def test_load_byte(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2()
data = b"abc"
ret = ifd.load_byte(data, legacy_api)
self.assertEqual(ret, b"abc" if legacy_api else (97, 98, 99))
self.assertEqual(ret, b"abc")

def test_load_string(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2()
Expand Down
12 changes: 11 additions & 1 deletion Tests/test_file_tiff_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def test_iccprofile(self):

im.save(out)
reloaded = Image.open(out)
self.assert_(type(im.info['icc_profile']) is not type(tuple))
self.assert_(type(im.info['icc_profile']) is not tuple)
self.assertEqual(im.info['icc_profile'], reloaded.info['icc_profile'])

def test_iccprofile_binary(self):
Expand All @@ -186,6 +186,16 @@ def test_iccprofile_binary(self):
self.assertEqual(im.tag_v2.tagtype[34675], 1)
self.assertTrue(im.info['icc_profile'])

def test_iccprofile_save_png(self):
im = Image.open('Tests/images/hopper.iccprofile.tif')
outfile = self.tempfile('temp.png')
im.save(outfile)

def test_iccprofile_binary_save_png(self):
im = Image.open('Tests/images/hopper.iccprofile_binary.tif')
outfile = self.tempfile('temp.png')
im.save(outfile)

def test_exif_div_zero(self):
im = hopper()
info = TiffImagePlugin.ImageFileDirectory_v2()
Expand Down
9 changes: 9 additions & 0 deletions docs/releasenotes/3.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,12 @@ There are two new options to control the ``build_ext`` task in ``setup.py``:
that are checked for libraries and headers for build systems or
cross compilers that specify that information in via environment
variables.


Image Metadata
==============

The return type for binary data in version 2 Exif and Tiff metadata
has been changed from a tuple of integers to bytes. This is a change
from the behavior since ``3.0.0``.

0 comments on commit ffcc067

Please sign in to comment.