mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-30 23:47: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 |         # 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