From 78c62727d3de1dcf9ae443e30e38b1ef30edf08c Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 30 Sep 2017 13:32:43 +1000 Subject: [PATCH] Moved seek frame position check into ImageFile --- PIL/DcxImagePlugin.py | 5 +++-- PIL/FliImagePlugin.py | 4 +--- PIL/GifImagePlugin.py | 4 ++-- PIL/ImImagePlugin.py | 7 +------ PIL/ImageFile.py | 10 ++++++++++ PIL/MicImagePlugin.py | 5 +++-- PIL/MpoImagePlugin.py | 15 +++++++-------- PIL/PsdImagePlugin.py | 7 ++++--- PIL/SpiderImagePlugin.py | 4 ++-- PIL/TiffImagePlugin.py | 8 +++++--- Tests/test_file_tiff.py | 3 ++- 11 files changed, 40 insertions(+), 32 deletions(-) diff --git a/PIL/DcxImagePlugin.py b/PIL/DcxImagePlugin.py index 797b8a5a8..0d920ad3c 100644 --- a/PIL/DcxImagePlugin.py +++ b/PIL/DcxImagePlugin.py @@ -59,6 +59,7 @@ class DcxImageFile(PcxImageFile): self._offset.append(offset) self.__fp = self.fp + self.frame = None self.seek(0) @property @@ -70,8 +71,8 @@ class DcxImageFile(PcxImageFile): return len(self._offset) > 1 def seek(self, frame): - if frame >= len(self._offset): - raise EOFError("attempt to seek outside DCX directory") + if not self._seek_check(frame): + return self.frame = frame self.fp = self.__fp self.fp.seek(self._offset[frame]) diff --git a/PIL/FliImagePlugin.py b/PIL/FliImagePlugin.py index 733d2d8f1..2c190b635 100644 --- a/PIL/FliImagePlugin.py +++ b/PIL/FliImagePlugin.py @@ -118,12 +118,10 @@ class FliImageFile(ImageFile.ImageFile): return self.__framecount > 1 def seek(self, frame): - if frame == self.__frame: + if not self._seek_check(frame): return if frame < self.__frame: self._seek(0) - if frame >= self.__framecount: - raise EOFError("no more images in FLI file") for f in range(self.__frame + 1, frame + 1): self._seek(f) diff --git a/PIL/GifImagePlugin.py b/PIL/GifImagePlugin.py index fc118e607..64764f99d 100644 --- a/PIL/GifImagePlugin.py +++ b/PIL/GifImagePlugin.py @@ -48,7 +48,7 @@ class GifImageFile(ImageFile.ImageFile): format = "GIF" format_description = "Compuserve GIF" _close_exclusive_fp_after_loading = False - + global_palette = None def data(self): @@ -117,7 +117,7 @@ class GifImageFile(ImageFile.ImageFile): return self._is_animated def seek(self, frame): - if frame == self.__frame: + if not self._seek_check(frame): return if frame < self.__frame: self._seek(0) diff --git a/PIL/ImImagePlugin.py b/PIL/ImImagePlugin.py index 3371b303f..3a1fdfbfc 100644 --- a/PIL/ImImagePlugin.py +++ b/PIL/ImImagePlugin.py @@ -270,11 +270,7 @@ class ImImageFile(ImageFile.ImageFile): return self.info[FRAMES] > 1 def seek(self, frame): - - if frame < 0 or frame >= self.info[FRAMES]: - raise EOFError("seek outside sequence") - - if self.frame == frame: + if not self._seek_check(frame): return self.frame = frame @@ -292,7 +288,6 @@ class ImImageFile(ImageFile.ImageFile): self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))] def tell(self): - return self.frame # diff --git a/PIL/ImageFile.py b/PIL/ImageFile.py index 5e1719fdc..051a61e6f 100644 --- a/PIL/ImageFile.py +++ b/PIL/ImageFile.py @@ -275,6 +275,16 @@ class ImageFile(Image.Image): # def load_read(self, bytes): # pass + def _seek_check(self, frame): + if (frame < 0 or + # Only check upper limit on frames if additional seek operations + # are not required to do so + (not (hasattr(self, "_n_frames") and self._n_frames is None) and + frame >= self.n_frames)): + raise EOFError("attempt to seek outside sequence") + + return self.tell() != frame + class StubImageFile(ImageFile): """ diff --git a/PIL/MicImagePlugin.py b/PIL/MicImagePlugin.py index 1b7972061..1dbb6a588 100644 --- a/PIL/MicImagePlugin.py +++ b/PIL/MicImagePlugin.py @@ -65,7 +65,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): raise SyntaxError("not an MIC file; no image entries") self.__fp = self.fp - self.frame = 0 + self.frame = None if len(self.images) > 1: self.category = Image.CONTAINER @@ -81,7 +81,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile): return len(self.images) > 1 def seek(self, frame): - + if not self._seek_check(frame): + return try: filename = self.images[frame] except IndexError: diff --git a/PIL/MpoImagePlugin.py b/PIL/MpoImagePlugin.py index b12307f00..8ad27b919 100644 --- a/PIL/MpoImagePlugin.py +++ b/PIL/MpoImagePlugin.py @@ -72,14 +72,13 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile): return self.__framecount > 1 def seek(self, frame): - if frame < 0 or frame >= self.__framecount: - raise EOFError("no more images in MPO file") - else: - self.fp = self.__fp - self.offset = self.__mpoffsets[frame] - self.tile = [ - ("jpeg", (0, 0) + self.size, self.offset, (self.mode, "")) - ] + if not self._seek_check(frame): + return + self.fp = self.__fp + self.offset = self.__mpoffsets[frame] + self.tile = [ + ("jpeg", (0, 0) + self.size, self.offset, (self.mode, "")) + ] self.__frame = frame def tell(self): diff --git a/PIL/PsdImagePlugin.py b/PIL/PsdImagePlugin.py index 1e4051c29..8603162e4 100644 --- a/PIL/PsdImagePlugin.py +++ b/PIL/PsdImagePlugin.py @@ -135,11 +135,12 @@ class PsdImageFile(ImageFile.ImageFile): return len(self.layers) > 1 def seek(self, layer): - # seek to given layer (1..max) - if layer == self.frame: + if not self._seek_check(layer): return + + # seek to given layer (1..max) try: - if layer <= 0: + if layer == 0: raise IndexError name, mode, bbox, tile = self.layers[layer-1] self.mode = mode diff --git a/PIL/SpiderImagePlugin.py b/PIL/SpiderImagePlugin.py index a821fcf87..5e5dde5a6 100644 --- a/PIL/SpiderImagePlugin.py +++ b/PIL/SpiderImagePlugin.py @@ -174,8 +174,8 @@ class SpiderImageFile(ImageFile.ImageFile): def seek(self, frame): if self.istack == 0: raise EOFError("attempt to seek in a non-stack file") - if frame >= self._nimages: - raise EOFError("attempt to seek past end of file") + if not self._seek_check(frame): + return self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes) self.fp = self.__fp self.fp.seek(self.stkoffset) diff --git a/PIL/TiffImagePlugin.py b/PIL/TiffImagePlugin.py index 81bb374ad..07fb97d14 100644 --- a/PIL/TiffImagePlugin.py +++ b/PIL/TiffImagePlugin.py @@ -555,8 +555,8 @@ class ImageFileDirectory_v2(collections.MutableMapping): # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed. # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple. # Don't mess with the legacy api, since it's frozen. - if ((info.length == 1) or - (info.length is None and len(values) == 1 and not legacy_api)): + if ((info.length == 1) or + (info.length is None and len(values) == 1 and not legacy_api)): # Don't mess with the legacy api, since it's frozen. if legacy_api and self.tagtype[tag] in [5, 10]: # rationals values = values, @@ -980,7 +980,9 @@ class TiffImageFile(ImageFile.ImageFile): def seek(self, frame): "Select a given frame as current image" - self._seek(max(frame, 0)) # Questionable backwards compatibility. + if not self._seek_check(frame): + return + self._seek(frame) # Create a new core image object on second and # subsequent frames in the image. Image may be # different size/mode. diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 758d76fd3..f8402b47c 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -349,13 +349,14 @@ class TestFileTiff(PillowTestCase): def test_seek(self): filename = "Tests/images/pil136.tiff" im = Image.open(filename) - im.seek(-1) + im.seek(0) self.assertEqual(im.tell(), 0) def test_seek_eof(self): filename = "Tests/images/pil136.tiff" im = Image.open(filename) self.assertEqual(im.tell(), 0) + self.assertRaises(EOFError, im.seek, -1) self.assertRaises(EOFError, im.seek, 1) def test__limit_rational_int(self):