Re: pillow patch
Claude Paroz <[email protected]> Mon, 28 Feb 2022 13:55:30 +0100
| Newsgroups | gmane.comp.python.reportlab.user |
|---|---|
| Message-ID | <[email protected]> |
Le 18.02.22 à 14:37, Claude Paroz a écrit : > Hi, > > As far as I can see, pillow is a required dependency of ReportLab, so > the attached patch simplifies some parts of the code to account for that. > I also upped the pillow minimal version as versions before 9.0.0 have > known security vulnerabilities. Attached is the same patch rebased after another conflicting patch was applied to main source tree. Claude -- www.2xlibre.net
0001-PIL-pillow-is-a-required-dependency.patch
(text/x-patch, 20 KB)
From f01f7f7578b7b37129cc4a27aad97e99cfaea153 Mon Sep 17 00:00:00 2001 From: Claude Paroz <[email protected]> Date: Mon, 14 Feb 2022 18:19:52 +0100 Subject: [PATCH] PIL (pillow) is a required dependency --- setup.py | 2 +- src/reportlab/lib/utils.py | 38 +---- src/reportlab/pdfgen/pdfimages.py | 3 +- tests/test_pdfgen_general.py | 257 ++++++++++++++---------------- tests/test_platypus_general.py | 14 +- tests/test_platypus_images.py | 13 +- 6 files changed, 137 insertions(+), 190 deletions(-) diff --git a/setup.py b/setup.py index 199b4193..4b0d3e9d 100644 --- a/setup.py +++ b/setup.py @@ -767,7 +767,7 @@ def main(): ], #this probably only works for setuptools, but distutils seems to ignore it - install_requires=['pillow>=4.0.0'], + install_requires=['pillow>=9.0.0'], python_requires='>=3.6, <4', extras_require={ 'RLPYCAIRO': ['rlPyCairo>=0.0.5'], diff --git a/src/reportlab/lib/utils.py b/src/reportlab/lib/utils.py index 811d6906..a9ad4754 100644 --- a/src/reportlab/lib/utils.py +++ b/src/reportlab/lib/utils.py @@ -11,6 +11,7 @@ from io import BytesIO from reportlab.lib.rltempfile import get_rl_tempfile, get_rl_tempdir from . rl_safe_eval import rl_safe_exec, rl_safe_eval, safer_globals +from PIL import Image class __UNSET__: @staticmethod @@ -389,19 +390,6 @@ def recursiveImport(modulename, baseDir=None, noCWD=0, debug=0): if debug: print('===== restore sys.path=%s' % repr(opath)) -# Image Capability Detection. Set a flag haveImages -# to tell us if PIL library is present. -# define PIL_Image as either None, or an alias for the PIL.Image -# module, as there are 2 ways to import it -try: - from PIL import Image -except ImportError: - try: - import Image - except ImportError: - Image = None -haveImages = Image is not None - class ArgvDictValue: '''A type to allow clients of getArgvDict to specify a conversion function''' def __init__(self,value,func): @@ -672,26 +660,12 @@ class ImageReader: self.fp.close() del self.fp #will become a property in the next statement self.__class__=LazyImageReader - if haveImages: - #detect which library we are using and open the image - if not self._image: - self._image = self._read_image(self.fp) - self.check_pil_image_size(self._image) - if getattr(self._image,'format',None)=='JPEG': self.jpeg_fh = self._jpeg_fh - else: - from reportlab.pdfbase.pdfutils import readJPEGInfo - try: - self._width,self._height, c, dpi = readJPEGInfo(self.fp) - except: - annotateException('\nImaging Library not available, unable to import bitmaps only jpegs\nfileName=%r identity=%s'%(fileName,self.identity())) - size = self._width*self._height*c - if self._max_image_size is not None and size>self._max+image_size: - raise MemoryError('JPEG %s color %s x %s image would use %s > %s bytes' - %(c,self._width,self._height,size,self._max_image_size)) + #detect which library we are using and open the image + if not self._image: + self._image = self._read_image(self.fp) + self.check_pil_image_size(self._image) + if getattr(self._image,'format',None)=='JPEG': self.jpeg_fh = self._jpeg_fh - self._data = self.fp.read() - self._dataA=None - self.fp.seek(0) except: annotateException('\nfileName=%r identity=%s'%(fileName,self.identity())) diff --git a/src/reportlab/pdfgen/pdfimages.py b/src/reportlab/pdfgen/pdfimages.py index d4dba07c..7c3ca202 100644 --- a/src/reportlab/pdfgen/pdfimages.py +++ b/src/reportlab/pdfgen/pdfimages.py @@ -11,7 +11,7 @@ import reportlab from reportlab import rl_config from reportlab.pdfbase import pdfutils from reportlab.pdfbase import pdfdoc -from reportlab.lib.utils import haveImages, isStr +from reportlab.lib.utils import isStr from reportlab.lib.rl_accel import fp_str, asciiBase85Encode from reportlab.lib.boxstuff import aspectRatioFix @@ -74,7 +74,6 @@ class PDFImage: def cache_imagedata(self): image = self.image if not pdfutils.cachedImageExists(image): - if not haveImages: return pdfutils.cacheImageFile(image) #now we have one cached, slurp it in diff --git a/tests/test_pdfgen_general.py b/tests/test_pdfgen_general.py index 599cbd88..1551168f 100644 --- a/tests/test_pdfgen_general.py +++ b/tests/test_pdfgen_general.py @@ -12,7 +12,7 @@ from io import BytesIO from reportlab.pdfgen import canvas # gmcm 2000/10/13, pdfgen now a package from reportlab.lib.units import inch, cm from reportlab.lib import colors -from reportlab.lib.utils import haveImages, fileName2FSEnc +from reportlab.lib.utils import fileName2FSEnc from reportlab.lib.boxstuff import rectCorner ################################################################# @@ -719,9 +719,6 @@ cost to performance.""") framePage(c, "Images") c.setFont('Times-Roman', 12) t = c.beginText(inch, 10 * inch) - if not haveImages: - c.drawString(inch, 11*inch, - "Python Imaging Library not found! Below you see rectangles instead of images.") t.textLines("""PDFgen uses the Python Imaging Library to process a very wide variety of image formats. @@ -736,28 +733,20 @@ cost to performance.""") c.drawText(t) - if haveImages: - from reportlab.lib.testutils import testsFolder - gif = os.path.join(testsFolder,'pythonpowered.gif') - c.drawInlineImage(gif,2*inch, 7*inch) - c.drawInlineImage(os.path.join(testsFolder,'pythonpowered-gs.gif'),4*inch, 7.5*inch) - tif = os.path.join(testsFolder,'test-cross.tiff') #example of a mode '1' image - c.drawInlineImage(tif,1*inch, 1*inch) - from reportlab.lib.utils import Image as PilImage - if PilImage: - c.drawInlineImage(PilImage.open(tif),1.25*inch, 1*inch) - else: - c.rect(2*inch, 7*inch, 110, 44) - c.rect(4*inch, 7*inch, 110, 44) + from reportlab.lib.testutils import testsFolder + gif = os.path.join(testsFolder,'pythonpowered.gif') + c.drawInlineImage(gif,2*inch, 7*inch) + c.drawInlineImage(os.path.join(testsFolder,'pythonpowered-gs.gif'),4*inch, 7.5*inch) + tif = os.path.join(testsFolder,'test-cross.tiff') #example of a mode '1' image + c.drawInlineImage(tif,1*inch, 1*inch) + from reportlab.lib.utils import Image as PilImage + c.drawInlineImage(PilImage.open(tif),1.25*inch, 1*inch) c.line(1.5*inch, 7*inch, 4*inch, 7*inch) c.line(2*inch, 6.5*inch, 2*inch, 8*inch) c.drawString(4.5 * inch, 7.25*inch, 'inline image drawn at natural size') - if haveImages: - c.drawInlineImage(gif,2*inch, 5*inch, inch, inch) - else: - c.rect(2*inch, 5*inch, inch, inch) + c.drawInlineImage(gif,2*inch, 5*inch, inch, inch) c.line(1.5*inch, 5*inch, 4*inch, 5*inch) c.line(2*inch, 4.5*inch, 2*inch, 6*inch) @@ -767,25 +756,17 @@ cost to performance.""") c.drawString(1.5 * inch, 3.75*inch, 'This results in faster generation and much smaller files.') for i in range(5): - if haveImages: - (w, h) = c.drawImage(gif, (1.5 + i)*inch, 3*inch) - else: - (w, h) = (144, 10) - c.rect((1.5 + i)*inch, 3*inch, 110, 44) + (w, h) = c.drawImage(gif, (1.5 + i)*inch, 3*inch) myMask = [254,255,222,223,0,1] c.drawString(1.5 * inch, 2.5*inch, "The optional 'mask' parameter lets you define transparent colors. We used a color picker") c.drawString(1.5 * inch, 2.3*inch, "to determine that the yellow in the image above is RGB=(225,223,0). We then define a mask") c.drawString(1.5 * inch, 2.1*inch, "spanning these RGB values: %s. The background vanishes!!" % myMask) c.drawString(2.5*inch, 1.2*inch, 'This would normally be obscured') - if haveImages: - c.drawImage(gif, 1*inch, 1.2*inch, w, h, mask=myMask) - c.drawImage(gif, 3*inch, 1.2*inch, w, h, mask='auto') - c.drawImage(os.path.join(testsFolder,'test-rgba.png'),5*inch,1.2*inch,width=10,height=10,mask='auto') - c.drawImage(os.path.join(testsFolder,'test-indexed.png'),5.5*inch,1.2*inch,width=10,height=10,mask='auto') - else: - c.rect(1*inch, 1.2*inch, w, h) - c.rect(3*inch, 1.2*inch, w, h) + c.drawImage(gif, 1*inch, 1.2*inch, w, h, mask=myMask) + c.drawImage(gif, 3*inch, 1.2*inch, w, h, mask='auto') + c.drawImage(os.path.join(testsFolder,'test-rgba.png'),5*inch,1.2*inch,width=10,height=10,mask='auto') + c.drawImage(os.path.join(testsFolder,'test-indexed.png'),5.5*inch,1.2*inch,width=10,height=10,mask='auto') c.showPage() c.drawString(1*inch, 10.25*inch, "For rgba type images we can use the alpha channel if we set mask='auto'.") @@ -804,114 +785,112 @@ cost to performance.""") c.rect(3*inch, 8+14.4*inch, w, h) c.rect(1*inch, 6+14.4*inch, w, h) c.rect(3*inch, 6+14.4*inch, w, h) - if haveImages: - from reportlab.lib.testutils import testsFolder - png = os.path.join(testsFolder,'solid_red_alpha.png') - c.drawImage(png, 1*inch, 8*inch+14.4, w, h, mask=None) - c.drawImage(png, 3*inch, 8*inch+14.4, w, h, mask='auto') - png = os.path.join(testsFolder,'alpha_test.png') - c.drawImage(png, 1*inch, 6*inch+14.4, w, h, mask=None) - c.drawImage(png, 3*inch, 6*inch+14.4, w, h, mask='auto') + from reportlab.lib.testutils import testsFolder + png = os.path.join(testsFolder,'solid_red_alpha.png') + c.drawImage(png, 1*inch, 8*inch+14.4, w, h, mask=None) + c.drawImage(png, 3*inch, 8*inch+14.4, w, h, mask='auto') + png = os.path.join(testsFolder,'alpha_test.png') + c.drawImage(png, 1*inch, 6*inch+14.4, w, h, mask=None) + c.drawImage(png, 3*inch, 6*inch+14.4, w, h, mask='auto') c.showPage() - if haveImages: - import shutil - c.drawString(1*inch, 10.25*inch, 'This jpeg is actually a gif') - jpg = outputfile('_i_am_actually_a_gif.jpg') - shutil.copyfile(gif,jpg) - c.drawImage(jpg, 1*inch, 9.25*inch, w, h, mask='auto') - tjpg = os.path.join(os.path.dirname(os.path.dirname(gif)),'docs','images','lj8100.jpg') - if os.path.isfile(tjpg): - c.drawString(4*inch, 10.25*inch, 'This gif is actually a jpeg') - tgif = outputfile(os.path.basename('_i_am_actually_a_jpeg.gif')) - shutil.copyfile(tjpg,tgif) - c.drawImage(tgif, 4*inch, 9.25*inch, w, h, mask='auto') - - c.drawString(inch, 9.0*inch, 'Image positioning tests with preserveAspectRatio') - - #preserveAspectRatio test - c.drawString(inch, 8.8*inch, 'Both of these should appear within the boxes, vertically centered') - - - x, y, w, h = inch, 6.75* inch, 2*inch, 2*inch - c.rect(x, y, w, h) - (w2, h2) = c.drawImage(gif, #anchor southwest, drawImage - x, y, width=w, height=h, - preserveAspectRatio=True, - anchor='c' - ) - - #now test drawInlineImage across the page - x = 5 * inch - c.rect(x, y, w, h) - (w2, h2) = c.drawInlineImage(gif, #anchor southwest, drawInlineImage - x, y, width=w, height=h, - preserveAspectRatio=True, - anchor='c' - ) - - c.drawString(inch, 5.75*inch, - 'anchored by respective corners - use both a wide and a tall one as tests') - x = 0.25 * inch - y = 5*inch - for anchor in ('nw','n','ne','w','c','e','sw','s','se'): - x += 0.75*inch - c.rect(x, y, 0.6*inch, 0.6*inch) - c.drawImage( - gif, x, y, - width=0.6*inch, height=0.6*inch, - preserveAspectRatio=True, - anchor=anchor - ) - c.drawString(x, y-0.1*inch, anchor) - - x = 0.25 * inch - y = 4*inch - tall_red = os.path.join(testsFolder,'tall_red.png') - for anchor in ('nw','n','ne','w','c','e','sw','s','se'): - x += 0.75*inch - c.rect(x, y, 0.6*inch, 0.6*inch) - c.drawImage( - tall_red, x, y, - width=0.6*inch, height=0.6*inch, - preserveAspectRatio=True, - anchor=anchor - ) - c.drawString(x, y-0.1*inch, anchor) - - #Andy's positioning code fails for this case when the image is not reaching the limit - x = 0.25 * inch - y = 3*inch - tall_red = os.path.join(testsFolder,'tall_red.png') - for anchor in ('nw','n','ne','w','c','e','sw','s','se'): - x += 0.75*inch - c.rect(x, 3*inch, 0.7*inch, 0.7*inch) - c.drawImage( - tall_red, x, 3*inch, - width=0.6*inch, height=0.6*inch, - preserveAspectRatio=True, - anchor=anchor - ) - c.drawString(x, y-0.1*inch, anchor) - - #fix by using anchorAtXY - x = 0.25 * inch - y = 2*inch - tall_red = os.path.join(testsFolder,'tall_red.png') - for anchor in ('nw','n','ne','w','c','e','sw','s','se'): - x += 0.75*inch - c.rect(x, y, 0.7*inch, 0.7*inch) - ax, ay = rectCorner(x, y, 0.7*inch, 0.7*inch, anchor) - c.drawImage( - tall_red, ax, ay, - width=0.6*inch, height=0.6*inch, - preserveAspectRatio=True, - anchor=anchor, - anchorAtXY=True, - ) - c.drawString(x, y-0.1*inch, anchor) + import shutil + c.drawString(1*inch, 10.25*inch, 'This jpeg is actually a gif') + jpg = outputfile('_i_am_actually_a_gif.jpg') + shutil.copyfile(gif,jpg) + c.drawImage(jpg, 1*inch, 9.25*inch, w, h, mask='auto') + tjpg = os.path.join(os.path.dirname(os.path.dirname(gif)),'docs','images','lj8100.jpg') + if os.path.isfile(tjpg): + c.drawString(4*inch, 10.25*inch, 'This gif is actually a jpeg') + tgif = outputfile(os.path.basename('_i_am_actually_a_jpeg.gif')) + shutil.copyfile(tjpg,tgif) + c.drawImage(tgif, 4*inch, 9.25*inch, w, h, mask='auto') + + c.drawString(inch, 9.0*inch, 'Image positioning tests with preserveAspectRatio') + + #preserveAspectRatio test + c.drawString(inch, 8.8*inch, 'Both of these should appear within the boxes, vertically centered') + + + x, y, w, h = inch, 6.75* inch, 2*inch, 2*inch + c.rect(x, y, w, h) + (w2, h2) = c.drawImage(gif, #anchor southwest, drawImage + x, y, width=w, height=h, + preserveAspectRatio=True, + anchor='c' + ) + + #now test drawInlineImage across the page + x = 5 * inch + c.rect(x, y, w, h) + (w2, h2) = c.drawInlineImage(gif, #anchor southwest, drawInlineImage + x, y, width=w, height=h, + preserveAspectRatio=True, + anchor='c' + ) + + c.drawString(inch, 5.75*inch, + 'anchored by respective corners - use both a wide and a tall one as tests') + x = 0.25 * inch + y = 5*inch + for anchor in ('nw','n','ne','w','c','e','sw','s','se'): + x += 0.75*inch + c.rect(x, y, 0.6*inch, 0.6*inch) + c.drawImage( + gif, x, y, + width=0.6*inch, height=0.6*inch, + preserveAspectRatio=True, + anchor=anchor + ) + c.drawString(x, y-0.1*inch, anchor) + + x = 0.25 * inch + y = 4*inch + tall_red = os.path.join(testsFolder,'tall_red.png') + for anchor in ('nw','n','ne','w','c','e','sw','s','se'): + x += 0.75*inch + c.rect(x, y, 0.6*inch, 0.6*inch) + c.drawImage( + tall_red, x, y, + width=0.6*inch, height=0.6*inch, + preserveAspectRatio=True, + anchor=anchor + ) + c.drawString(x, y-0.1*inch, anchor) + + #Andy's positioning code fails for this case when the image is not reaching the limit + x = 0.25 * inch + y = 3*inch + tall_red = os.path.join(testsFolder,'tall_red.png') + for anchor in ('nw','n','ne','w','c','e','sw','s','se'): + x += 0.75*inch + c.rect(x, 3*inch, 0.7*inch, 0.7*inch) + c.drawImage( + tall_red, x, 3*inch, + width=0.6*inch, height=0.6*inch, + preserveAspectRatio=True, + anchor=anchor + ) + c.drawString(x, y-0.1*inch, anchor) + + #fix by using anchorAtXY + x = 0.25 * inch + y = 2*inch + tall_red = os.path.join(testsFolder,'tall_red.png') + for anchor in ('nw','n','ne','w','c','e','sw','s','se'): + x += 0.75*inch + c.rect(x, y, 0.7*inch, 0.7*inch) + ax, ay = rectCorner(x, y, 0.7*inch, 0.7*inch, anchor) + c.drawImage( + tall_red, ax, ay, + width=0.6*inch, height=0.6*inch, + preserveAspectRatio=True, + anchor=anchor, + anchorAtXY=True, + ) + c.drawString(x, y-0.1*inch, anchor) - c.showPage() + c.showPage() ######################################################################### diff --git a/tests/test_platypus_general.py b/tests/test_platypus_general.py index ecb611d6..68de73a3 100644 --- a/tests/test_platypus_general.py +++ b/tests/test_platypus_general.py @@ -24,16 +24,14 @@ from reportlab.lib.units import inch, cm from reportlab.lib.styles import PropertySet, getSampleStyleSheet, ParagraphStyle from reportlab.lib import colors from reportlab.rl_config import defaultPageSize -from reportlab.lib.utils import haveImages, _RL_DIR, rl_isfile, open_for_read, fileName2FSEnc, asNative +from reportlab.lib.utils import _RL_DIR, rl_isfile, open_for_read, fileName2FSEnc, asNative import unittest from reportlab.lib.testutils import testsFolder -if haveImages: - _GIF = os.path.join(testsFolder,'pythonpowered.gif') - if not rl_isfile(_GIF): _GIF = None - _GAPNG = os.path.join(testsFolder,'gray-alpha.png') - if not rl_isfile(_GAPNG): _GAPNG = None -else: - _GIF = None + +_GIF = os.path.join(testsFolder,'pythonpowered.gif') +if not rl_isfile(_GIF): _GIF = None +_GAPNG = os.path.join(testsFolder,'gray-alpha.png') +if not rl_isfile(_GAPNG): _GAPNG = None if _GIF: _GIFFSEnc=fileName2FSEnc(_GIF) if _GAPNG: _GAPNGFSEnc=fileName2FSEnc(_GAPNG) diff --git a/tests/test_platypus_images.py b/tests/test_platypus_images.py index e8723616..dfe3ed0d 100644 --- a/tests/test_platypus_images.py +++ b/tests/test_platypus_images.py @@ -15,16 +15,13 @@ def run(): ShowBoundaryValue, SimpleDocTemplate, FrameBG, Paragraph, \ FrameBreak from reportlab.lib.colors import toColor - from reportlab.lib.utils import haveImages, _RL_DIR, rl_isfile, open_for_read, fileName2FSEnc, asNative + from reportlab.lib.utils import _RL_DIR, rl_isfile, open_for_read, fileName2FSEnc, asNative from reportlab.lib.styles import getSampleStyleSheet styleSheet = getSampleStyleSheet() - if haveImages: - _GIF = os.path.join(testsFolder,'pythonpowered.gif') - if not rl_isfile(_GIF): _GIF = None - _GAPNG = os.path.join(testsFolder,'gray-alpha.png') - if not rl_isfile(_GAPNG): _GAPNG = None - else: - _GIF = None + _GIF = os.path.join(testsFolder,'pythonpowered.gif') + if not rl_isfile(_GIF): _GIF = None + _GAPNG = os.path.join(testsFolder,'gray-alpha.png') + if not rl_isfile(_GAPNG): _GAPNG = None if _GIF: _GIFFSEnc=fileName2FSEnc(_GIF) if _GAPNG: _GAPNGFSEnc=fileName2FSEnc(_GAPNG) -- 2.30.2