mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 09:14:27 +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
|
||||
# based on the data in the strip.
|
||||
# ICCPROFILE crashes.
|
||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS, ICCPROFILE]
|
||||
blocklist = [STRIPOFFSETS, STRIPBYTECOUNTS]
|
||||
atts = {}
|
||||
# bits per sample is a single short in the tiff directory, not a list.
|
||||
atts[BITSPERSAMPLE] = bits[0]
|
||||
|
@ -1411,16 +1410,22 @@ def _save(im, fp, filename):
|
|||
legacy_ifd = {}
|
||||
if hasattr(im, 'tag'):
|
||||
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(),
|
||||
legacy_ifd.items()):
|
||||
if k not in atts and k not in blocklist:
|
||||
if isinstance(v, unicode if bytes is str else str):
|
||||
atts[k] = v.encode('ascii', 'replace') + b"\0"
|
||||
elif isinstance(v, IFDRational):
|
||||
atts[k] = float(v)
|
||||
# Libtiff can only process certain core items without adding
|
||||
# them to the custom dictionary. It will segfault if it attempts
|
||||
# to add a custom tag without the dictionary entry
|
||||
#
|
||||
# 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:
|
||||
atts[k] = v
|
||||
atts[tag] = value
|
||||
|
||||
if DEBUG:
|
||||
print("Converted items: %s" % sorted(atts.items()))
|
||||
|
|
|
@ -331,3 +331,72 @@ TYPES = {}
|
|||
# 11: "float",
|
||||
# 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 os
|
||||
|
||||
from PIL import Image, TiffImagePlugin
|
||||
from PIL import Image, TiffImagePlugin, TiffTags
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -172,6 +172,59 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
for field in requested_fields:
|
||||
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):
|
||||
i = Image.open('Tests/images/hopper_g4_500.tif')
|
||||
out = self.tempfile("temp.tif")
|
||||
|
@ -395,6 +448,17 @@ class TestFileLibTiff(LibTiffTestCase):
|
|||
TiffImagePlugin.WRITE_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__':
|
||||
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
|
||||
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
|
||||
numbers are returned as a :py:class:`~PIL.TiffImagePlugin.IFDRational`
|
||||
object.
|
||||
|
@ -542,13 +542,19 @@ The :py:meth:`~PIL.Image.Image.save` method can take the following keyword argum
|
|||
|
||||
.. 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
|
||||
file. (valid only with libtiff installed) Valid compression
|
||||
methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``,
|
||||
``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``,
|
||||
``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``,
|
||||
``"tiff_sgilog24"``, ``"tiff_raw_16"``
|
||||
file. (valid only with libtiff installed) Valid compression
|
||||
methods are: ``None``, ``"tiff_ccitt"``, ``"group3"``,
|
||||
``"group4"``, ``"tiff_jpeg"``, ``"tiff_adobe_deflate"``,
|
||||
``"tiff_thunderscan"``, ``"tiff_deflate"``, ``"tiff_sgilog"``,
|
||||
``"tiff_sgilog24"``, ``"tiff_raw_16"``
|
||||
|
||||
These arguments to set the tiff header fields are an alternative to
|
||||
using the general tags available through tiffinfo.
|
||||
|
|
Loading…
Reference in New Issue
Block a user