From beb7b4d0f6db44dcae71a02c6dd4f3c39da03992 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 10 Sep 2022 22:50:54 +1000 Subject: [PATCH 1/2] Added reading of TIFF child images --- Tests/images/child_ifd.tiff | Bin 0 -> 2971 bytes Tests/images/child_ifd_jpeg.tiff | Bin 0 -> 830 bytes Tests/test_file_tiff.py | 17 ++++++++++++++++ src/PIL/TiffImagePlugin.py | 33 +++++++++++++++++++++++++++++++ src/PIL/TiffTags.py | 1 + 5 files changed, 51 insertions(+) create mode 100644 Tests/images/child_ifd.tiff create mode 100644 Tests/images/child_ifd_jpeg.tiff diff --git a/Tests/images/child_ifd.tiff b/Tests/images/child_ifd.tiff new file mode 100644 index 0000000000000000000000000000000000000000..700185d88ae6b504dfef29689573fe6afcb4ff73 GIT binary patch literal 2971 zcmeH|NlX(_7{}kto2{jqPH91r#8N;jA;wZf@xZ2tgamLwx#6w{ z6A;&nLB;K0L~$b?1UJBosNfP0;98BQe%~;oH8fT{m_Yd2@6CVaecyZUdvAWN%N0W; zLWqeFh9e9pD>&n$S%wk{&-%bAXp9lT1yu{6$1^Mmh3i{zSCBtg`Pf*=J+Iz1XFLy+Ep zH#o>(D$7&K>qS9SLWb3NuM$}zR$`@$jj%F9Wyae|Cc`>;WqG(OiZLwZctM0^1VKQv z5tdIWESiTELv#i76Hew$5snlgI5S(c6`JF!>guJ))TS0oR(pSxGpDFprw<98FflB8 z%2fNbnE06qvl3?~rOla}K5xD&W8tF3OO`HMzGC&7T=&|%{B^}88#b11D%-qe>$dGR zJ9gIY+P!D*zWoOd9%?w!c=Xuu6DLodIeV`8{Dq5`E?>LedgJD;+jrVJ?mu|==y7LP zch9ruFM9i4zIy#;;NANVA3uHeeEEuXG4Mib=C>{x>SFN>M68QpOTnqk@evNel#(fC z7Y5tnsw8u2U42uFF4CFRZ^pbg%RKH@&@~QuAbQ7m?wV26;C^UNQkwfJBQ0Sz6$uJ50`Ds8wq?HiE)f?<#%iJ@clc} z_>ZnJjap;+Fx>o!X$!Uiq$jrbD6rTjd%s@6w4I7lr1QZv9s-FR5i$cXg zdc=^-5r?ut;xb6)ctOQy1I0mr0}O!-CI&_(h?91Lcu;mL$UY$Wf8+lH1_3WOPd5fe zppzIu?)rb5!I^=Bjg6g+m4ls~os*M;i${c)hnt&6Qb?FzL{>^(PF6}rMnOeST|r4l zSw=>~TvNxu(8R<ch$n!2CT12^Hg*n9E^eTLtpW_d;AUoGVP<7zVFAk4 z0_7Q41X+a?4ISBp0~6Vm3Pp?>CobercG`GQH0a_772~9$CQdFfaS2H&RW)@DO)V2s zGjj_|D`yv1H+K(Dui%i-u<(e;sN|H?wDgS3tm2Z=vhs?`s^*r~w)T$Bu1S-pOr17; z#>`oZ7B5-4Z25|nt2S-kvUS_`9Xod&I(+2lvEwIBp1O4T%GGPvZ`{1~@X_NZPoF)1 z@$%KjPoKYh{r3IG&tD*aF#=Nt3_yH_<}X2@znEB9m|56C{$gY*2YFnOg;mjzO~^5j zJ+V+&$*7S-#A)KfjR!fEje|ajCKX-e5>qjGsQMA)HL%Z!^H>vEK7)G<;jdc^Jj{&1 c$YT~{uxEJmVo}%6$=?MQb!jj_G4ubM03_ns#{d8T literal 0 HcmV?d00001 diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 8706cb950dd..f98b0bc91bf 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -84,6 +84,23 @@ def test_context_manager(self): with Image.open("Tests/images/multipage.tiff") as im: im.load() + def test_get_child_images(self): + def check(ims, sizes): + assert len(ims) == len(sizes) + + for i, im in enumerate(ims): + w = sizes[i] + expected = Image.new("RGB", (w, w), "#f00") + assert_image_similar(im, expected, 1) + + with Image.open("Tests/images/child_ifd.tiff") as im: + ims = im.get_child_images() + check(ims, (16, 8)) + + with Image.open("Tests/images/child_ifd_jpeg.tiff") as im: + ims = im.get_child_images() + check(ims, (20,)) + def test_mac_tiff(self): # Read RGBa images from macOS [@PIL136] diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index f2f1299122e..766d46ffb23 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -1148,6 +1148,39 @@ def tell(self): """Return the current frame number""" return self.__frame + def get_child_images(self): + if SUBIFD not in self.tag_v2: + return [] + child_images = [] + exif = self.getexif() + offset = None + for im_offset in self.tag_v2[SUBIFD]: + # reset buffered io handle in case fp + # was passed to libtiff, invalidating the buffer + current_offset = self._fp.tell() + if offset is None: + offset = current_offset + + fp = self._fp + ifd = exif._get_ifd_dict(im_offset) + jpegInterchangeFormat = ifd.get(513) + if jpegInterchangeFormat is not None: + fp.seek(jpegInterchangeFormat) + jpeg_data = fp.read(ifd.get(514)) + + fp = io.BytesIO(jpeg_data) + + with Image.open(fp) as im: + if jpegInterchangeFormat is None: + im._frame_pos = [im_offset] + im._seek(0) + im.load() + child_images.append(im) + + if offset is not None: + self._fp.seek(offset) + return child_images + def getxmp(self): """ Returns a dictionary containing the XMP tags. diff --git a/src/PIL/TiffTags.py b/src/PIL/TiffTags.py index e3094b4db47..3f3a1ccd2a7 100644 --- a/src/PIL/TiffTags.py +++ b/src/PIL/TiffTags.py @@ -160,6 +160,7 @@ def lookup(tag, group=None): 323: ("TileLength", LONG, 1), 324: ("TileOffsets", LONG, 0), 325: ("TileByteCounts", LONG, 0), + 330: ("SubIFDs", LONG, 0), 332: ("InkSet", SHORT, 1), 333: ("InkNames", ASCII, 1), 334: ("NumberOfInks", SHORT, 1), From ed016f8f5a0d6aed98897bcb992b937a1d9d7d18 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sun, 11 Sep 2022 09:20:45 +1000 Subject: [PATCH 2/2] Parametrized test --- Tests/test_file_tiff.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index f98b0bc91bf..ac0bd7f6096 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -84,22 +84,23 @@ def test_context_manager(self): with Image.open("Tests/images/multipage.tiff") as im: im.load() - def test_get_child_images(self): - def check(ims, sizes): - assert len(ims) == len(sizes) - - for i, im in enumerate(ims): - w = sizes[i] - expected = Image.new("RGB", (w, w), "#f00") - assert_image_similar(im, expected, 1) - - with Image.open("Tests/images/child_ifd.tiff") as im: + @pytest.mark.parametrize( + "path, sizes", + ( + ("Tests/images/hopper.tif", ()), + ("Tests/images/child_ifd.tiff", (16, 8)), + ("Tests/images/child_ifd_jpeg.tiff", (20,)), + ), + ) + def test_get_child_images(self, path, sizes): + with Image.open(path) as im: ims = im.get_child_images() - check(ims, (16, 8)) - with Image.open("Tests/images/child_ifd_jpeg.tiff") as im: - ims = im.get_child_images() - check(ims, (20,)) + assert len(ims) == len(sizes) + for i, im in enumerate(ims): + w = sizes[i] + expected = Image.new("RGB", (w, w), "#f00") + assert_image_similar(im, expected, 1) def test_mac_tiff(self): # Read RGBa images from macOS [@PIL136]