mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-02-04 21:50:54 +03:00
Merge pull request #1620 from wiredfool/issue_1597
Partial fix for #1597
This commit is contained in:
commit
e5076a3278
|
@ -1400,8 +1400,7 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
# STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
|
# STRIPOFFSETS and STRIPBYTECOUNTS are added by the library
|
||||||
# based on the data in the strip.
|
# based on the data in the strip.
|
||||||
# ICCPROFILE crashes.
|
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS]
|
||||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ICCPROFILE]
|
|
||||||
atts = {}
|
atts = {}
|
||||||
# bits per sample is a single short in the tiff directory, not a list.
|
# bits per sample is a single short in the tiff directory, not a list.
|
||||||
atts[BITSPERSAMPLE] = bits[0]
|
atts[BITSPERSAMPLE] = bits[0]
|
||||||
|
@ -1411,16 +1410,22 @@ def _save(im, fp, filename):
|
||||||
legacy_ifd = {}
|
legacy_ifd = {}
|
||||||
if hasattr(im, 'tag'):
|
if hasattr(im, 'tag'):
|
||||||
legacy_ifd = im.tag.to_v2()
|
legacy_ifd = im.tag.to_v2()
|
||||||
for k, v in itertools.chain(ifd.items(),
|
for tag, value in itertools.chain(ifd.items(),
|
||||||
getattr(im, 'tag_v2', {}).items(),
|
getattr(im, 'tag_v2', {}).items(),
|
||||||
legacy_ifd.items()):
|
legacy_ifd.items()):
|
||||||
if k not in atts and k not in blocklist:
|
# Libtiff can only process certain core items without adding
|
||||||
if isinstance(v, unicode if bytes is str else str):
|
# them to the custom dictionary. It will segfault if it attempts
|
||||||
atts[k] = v.encode('ascii', 'replace') + b"\0"
|
# to add a custom tag without the dictionary entry
|
||||||
elif isinstance(v, IFDRational):
|
#
|
||||||
atts[k] = float(v)
|
# UNDONE -- add code for the custom dictionary
|
||||||
|
if tag not in TiffTags.LIBTIFF_CORE: continue
|
||||||
|
if tag not in atts and tag not in blocklist:
|
||||||
|
if isinstance(value, unicode if bytes is str else str):
|
||||||
|
atts[tag] = value.encode('ascii', 'replace') + b"\0"
|
||||||
|
elif isinstance(value, IFDRational):
|
||||||
|
atts[tag] = float(value)
|
||||||
else:
|
else:
|
||||||
atts[k] = v
|
atts[tag] = value
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
print("Converted items: %s" % sorted(atts.items()))
|
print("Converted items: %s" % sorted(atts.items()))
|
||||||
|
|
|
@ -331,3 +331,72 @@ TYPES = {}
|
||||||
# 11: "float",
|
# 11: "float",
|
||||||
# 12: "double",
|
# 12: "double",
|
||||||
# }
|
# }
|
||||||
|
|
||||||
|
#
|
||||||
|
# These tags are handled by default in libtiff, without
|
||||||
|
# adding to the custom dictionary. From tif_dir.c, searching for
|
||||||
|
# case TIFFTAG in the _TIFFVSetField function:
|
||||||
|
# Line: item.
|
||||||
|
# 148: case TIFFTAG_SUBFILETYPE:
|
||||||
|
# 151: case TIFFTAG_IMAGEWIDTH:
|
||||||
|
# 154: case TIFFTAG_IMAGELENGTH:
|
||||||
|
# 157: case TIFFTAG_BITSPERSAMPLE:
|
||||||
|
# 181: case TIFFTAG_COMPRESSION:
|
||||||
|
# 202: case TIFFTAG_PHOTOMETRIC:
|
||||||
|
# 205: case TIFFTAG_THRESHHOLDING:
|
||||||
|
# 208: case TIFFTAG_FILLORDER:
|
||||||
|
# 214: case TIFFTAG_ORIENTATION:
|
||||||
|
# 221: case TIFFTAG_SAMPLESPERPIXEL:
|
||||||
|
# 228: case TIFFTAG_ROWSPERSTRIP:
|
||||||
|
# 238: case TIFFTAG_MINSAMPLEVALUE:
|
||||||
|
# 241: case TIFFTAG_MAXSAMPLEVALUE:
|
||||||
|
# 244: case TIFFTAG_SMINSAMPLEVALUE:
|
||||||
|
# 247: case TIFFTAG_SMAXSAMPLEVALUE:
|
||||||
|
# 250: case TIFFTAG_XRESOLUTION:
|
||||||
|
# 256: case TIFFTAG_YRESOLUTION:
|
||||||
|
# 262: case TIFFTAG_PLANARCONFIG:
|
||||||
|
# 268: case TIFFTAG_XPOSITION:
|
||||||
|
# 271: case TIFFTAG_YPOSITION:
|
||||||
|
# 274: case TIFFTAG_RESOLUTIONUNIT:
|
||||||
|
# 280: case TIFFTAG_PAGENUMBER:
|
||||||
|
# 284: case TIFFTAG_HALFTONEHINTS:
|
||||||
|
# 288: case TIFFTAG_COLORMAP:
|
||||||
|
# 294: case TIFFTAG_EXTRASAMPLES:
|
||||||
|
# 298: case TIFFTAG_MATTEING:
|
||||||
|
# 305: case TIFFTAG_TILEWIDTH:
|
||||||
|
# 316: case TIFFTAG_TILELENGTH:
|
||||||
|
# 327: case TIFFTAG_TILEDEPTH:
|
||||||
|
# 333: case TIFFTAG_DATATYPE:
|
||||||
|
# 344: case TIFFTAG_SAMPLEFORMAT:
|
||||||
|
# 361: case TIFFTAG_IMAGEDEPTH:
|
||||||
|
# 364: case TIFFTAG_SUBIFD:
|
||||||
|
# 376: case TIFFTAG_YCBCRPOSITIONING:
|
||||||
|
# 379: case TIFFTAG_YCBCRSUBSAMPLING:
|
||||||
|
# 383: case TIFFTAG_TRANSFERFUNCTION:
|
||||||
|
# 389: case TIFFTAG_REFERENCEBLACKWHITE:
|
||||||
|
# 393: case TIFFTAG_INKNAMES:
|
||||||
|
|
||||||
|
# some of these are not in our TAGS_V2 dict and were included from tiff.h
|
||||||
|
|
||||||
|
LIBTIFF_CORE = set ([255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
|
||||||
|
278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
|
||||||
|
296, 297, 321, 320, 338, 32995, 322, 323, 32998,
|
||||||
|
32996, 339, 32997, 330, 531, 530, 301, 532, 333,
|
||||||
|
# as above
|
||||||
|
269 # this has been in our tests forever, and works
|
||||||
|
])
|
||||||
|
|
||||||
|
LIBTIFF_CORE.remove(320) # Array of short, crashes
|
||||||
|
LIBTIFF_CORE.remove(301) # Array of short, crashes
|
||||||
|
LIBTIFF_CORE.remove(532) # Array of long, crashes
|
||||||
|
|
||||||
|
LIBTIFF_CORE.remove(255) # We don't have support for subfiletypes
|
||||||
|
LIBTIFF_CORE.remove(322) # We don't have support for tiled images in libtiff
|
||||||
|
LIBTIFF_CORE.remove(323) # Tiled images
|
||||||
|
LIBTIFF_CORE.remove(333) # Ink Names either
|
||||||
|
|
||||||
|
# Note to advanced users: There may be combinations of these
|
||||||
|
# parameters and values that when added properly, will work and
|
||||||
|
# produce valid tiff images that may work in your application.
|
||||||
|
# It is safe to add and remove tags from this set from Pillow's point
|
||||||
|
# of view so long as you test against libtiff.
|
||||||
|
|
BIN
Tests/images/rdf.tif
Normal file
BIN
Tests/images/rdf.tif
Normal file
Binary file not shown.
|
@ -7,7 +7,7 @@ import logging
|
||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PIL import Image, TiffImagePlugin
|
from PIL import Image, TiffImagePlugin, TiffTags
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -172,6 +172,59 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
for field in requested_fields:
|
for field in requested_fields:
|
||||||
self.assertTrue(field in reloaded, "%s not in metadata" % field)
|
self.assertTrue(field in reloaded, "%s not in metadata" % field)
|
||||||
|
|
||||||
|
def test_additional_metadata(self):
|
||||||
|
# these should not crash. Seriously dummy data, most of it doesn't make
|
||||||
|
# any sense, so we're running up against limits where we're asking
|
||||||
|
# libtiff to do stupid things.
|
||||||
|
|
||||||
|
# Get the list of the ones that we should be able to write
|
||||||
|
|
||||||
|
core_items = dict((tag, info) for tag, info in [(s,TiffTags.lookup(s)) for s
|
||||||
|
in TiffTags.LIBTIFF_CORE]
|
||||||
|
if info.type is not None)
|
||||||
|
|
||||||
|
# Exclude ones that have special meaning that we're already testing them
|
||||||
|
im = Image.open('Tests/images/hopper_g4.tif')
|
||||||
|
for tag in im.tag_v2.keys():
|
||||||
|
try:
|
||||||
|
del(core_items[tag])
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
# Type codes:
|
||||||
|
# 2: "ascii",
|
||||||
|
# 3: "short",
|
||||||
|
# 4: "long",
|
||||||
|
# 5: "rational",
|
||||||
|
# 12: "double",
|
||||||
|
# type: dummy value
|
||||||
|
values = { 2: 'test',
|
||||||
|
3: 1,
|
||||||
|
4: 2**20,
|
||||||
|
5: TiffImagePlugin.IFDRational(100,1),
|
||||||
|
12: 1.05 }
|
||||||
|
|
||||||
|
|
||||||
|
new_ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
for tag, info in core_items.items():
|
||||||
|
if info.length == 1:
|
||||||
|
new_ifd[tag] = values[info.type]
|
||||||
|
if info.length == 0:
|
||||||
|
new_ifd[tag] = tuple(values[info.type] for _ in range(3))
|
||||||
|
else:
|
||||||
|
new_ifd[tag] = tuple(values[info.type] for _ in range(info.length))
|
||||||
|
|
||||||
|
# Extra samples really doesn't make sense in this application.
|
||||||
|
del(new_ifd[338])
|
||||||
|
|
||||||
|
out = self.tempfile("temp.tif")
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
|
||||||
|
im.save(out, tiffinfo=new_ifd)
|
||||||
|
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_g3_compression(self):
|
def test_g3_compression(self):
|
||||||
i = Image.open('Tests/images/hopper_g4_500.tif')
|
i = Image.open('Tests/images/hopper_g4_500.tif')
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
|
@ -395,6 +448,17 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
|
def test_crashing_metadata(self):
|
||||||
|
# issue 1597
|
||||||
|
im = Image.open('Tests/images/rdf.tif')
|
||||||
|
out = self.tempfile('temp.tif')
|
||||||
|
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
|
# this shouldn't crash
|
||||||
|
im.save(out, format='TIFF')
|
||||||
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -498,7 +498,7 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
|
||||||
|
|
||||||
The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary
|
The :py:attr:`~PIL.Image.Image.tag_v2` attribute contains a dictionary
|
||||||
of TIFF metadata. The keys are numerical indexes from
|
of TIFF metadata. The keys are numerical indexes from
|
||||||
`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single
|
:py:attr:`~PIL.TiffTags.TAGS_V2`. Values are strings or numbers for single
|
||||||
items, multiple values are returned in a tuple of values. Rational
|
items, multiple values are returned in a tuple of values. Rational
|
||||||
numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational`
|
numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational`
|
||||||
object.
|
object.
|
||||||
|
@ -542,13 +542,19 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
|
||||||
|
|
||||||
.. versionadded:: 3.0.0
|
.. versionadded:: 3.0.0
|
||||||
|
|
||||||
**compression**
|
.. note::
|
||||||
|
|
||||||
|
Only some tags are currently supported when writing using
|
||||||
|
libtiff. The supported list is found in
|
||||||
|
:py:attr:`~PIL:TiffTags.LIBTIFF_CORE`.
|
||||||
|
|
||||||
|
**compression**
|
||||||
A string containing the desired compression method for the
|
A string containing the desired compression method for the
|
||||||
file. (valid only with libtiff installed) Valid compression
|
file. (valid only with libtiff installed) Valid compression
|
||||||
methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``,
|
methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``,
|
||||||
``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``,
|
``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``,
|
||||||
``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``,
|
``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``,
|
||||||
``"tiff_sgilog24"``, ``"tiff_raw_16"``
|
``"tiff_sgilog24"``, ``"tiff_raw_16"``
|
||||||
|
|
||||||
These arguments to set the tiff header fields are an alternative to
|
These arguments to set the tiff header fields are an alternative to
|
||||||
using the general tags available through tiffinfo.
|
using the general tags available through tiffinfo.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user