diff --git a/AUTHORS b/AUTHORS index b9b9bb4..498f447 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,6 +15,7 @@ alphabetical order): - Christophe-Marie Duquesne - Craig Gallek - @datro +- David Schultz - David Siroky - Edward Betts - Edwin Steele diff --git a/sigal/gallery.py b/sigal/gallery.py index aa10419..a2edff1 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -619,6 +619,9 @@ class Gallery: self.init_pool(ncpu) check_or_create_dir(settings['destination']) + if settings['max_img_pixels']: + PILImage.MAX_IMAGE_PIXELS = settings['max_img_pixels'] + # Build the list of directories with images albums = self.albums = {} src_path = self.settings['source'] @@ -708,7 +711,11 @@ class Gallery: self.logger.info("Using %s cores", ncpu) if ncpu > 1: - self.pool = multiprocessing.Pool(processes=ncpu) + def pool_init(): + if self.settings['max_img_pixels']: + PILImage.MAX_IMAGE_PIXELS = self.settings['max_img_pixels'] + self.pool = multiprocessing.Pool(processes=ncpu, + initializer=pool_init) else: self.pool = None @@ -756,8 +763,6 @@ class Gallery: for status in self.pool.imap_unordered(worker, media_list): result.append(status) bar.update(1) - self.pool.close() - self.pool.join() except KeyboardInterrupt: self.pool.terminate() sys.exit('Interrupted') @@ -768,6 +773,9 @@ class Gallery: "defined in the settings file, which can't be serialized.", exc_info=True) sys.exit('Abort') + finally: + self.pool.close() + self.pool.join() else: with progressbar(media_list, **bar_opt) as medias: result = [process_file(media_item) for media_item in medias] diff --git a/sigal/settings.py b/sigal/settings.py index df8bbd1..5a51060 100644 --- a/sigal/settings.py +++ b/sigal/settings.py @@ -52,6 +52,7 @@ _DEFAULT_CONFIG = { 'links': '', 'locale': '', 'make_thumbs': True, + 'max_img_pixels': None, 'medias_sort_attr': 'filename', 'medias_sort_reverse': False, 'mp4_options': ['-crf', '23', '-strict', '-2'], diff --git a/sigal/templates/sigal.conf.py b/sigal/templates/sigal.conf.py index 6230ac3..ee4ce42 100644 --- a/sigal/templates/sigal.conf.py +++ b/sigal/templates/sigal.conf.py @@ -46,6 +46,11 @@ theme = 'galleria' # Size of resized image (default: (640, 480)) img_size = (800, 600) +# Max input image size in pixels (default: None, i.e. use PIL default) +# This option sets `PIL.Image.MAX_IMAGE_PIXELS` in case you want to +# convert/resize very large images. +# max_img_pixels = None + # Output format of images (default: None, i.e. use input format) # img_format = "JPEG" diff --git a/tests/test_gallery.py b/tests/test_gallery.py index 4255a3f..2780f75 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -4,6 +4,7 @@ import os from os.path import join import pytest +from PIL import Image as PILImage from sigal.gallery import Album, Gallery, Image, Media, Video from sigal.video import SubprocessException @@ -289,6 +290,31 @@ def test_gallery(settings, tmpdir): logger.setLevel(logging.INFO) +def test_gallery_max_img_pixels(settings, tmpdir, monkeypatch): + "Test the Gallery class with the max_img_pixels setting." + monkeypatch.setattr('PIL.Image.MAX_IMAGE_PIXELS', 100000000) + + with open(str(tmpdir.join('my.css')), mode='w') as f: + f.write('color: red') + + settings['destination'] = str(tmpdir) + settings['user_css'] = str(tmpdir.join('my.css')) + settings['max_img_pixels'] = 5000 + + logger = logging.getLogger('sigal') + logger.setLevel(logging.DEBUG) + try: + with pytest.raises(PILImage.DecompressionBombError): + gal = Gallery(settings, ncpu=1) + gal.build() + + settings['max_img_pixels'] = 100000000 + gal = Gallery(settings, ncpu=1) + gal.build() + finally: + logger.setLevel(logging.INFO) + + def test_empty_dirs(settings): gal = Gallery(settings, ncpu=1) assert 'empty' not in gal.albums