Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ install_requires =
click
Jinja2
Markdown
Pillow>=4.0.0
Pillow>=6.0.0
pilkit
natsort

Expand Down
23 changes: 13 additions & 10 deletions sigal/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from PIL import Image as PILImage
from PIL import ImageFile, ImageOps, IptcImagePlugin
from PIL.ExifTags import GPSTAGS, TAGS
from PIL.Image import Exif
from pilkit.processors import Transpose
from pilkit.utils import save_image

Expand Down Expand Up @@ -72,7 +73,7 @@ def _read_image(file_path):
im = PILImage.open(file_path)

for w in caught_warnings:
logger.warning(
logger.info(
f'PILImage reported a warning for file {file_path}\n'
f'{w.category}: {w.message}'
)
Expand All @@ -98,27 +99,29 @@ def generate_image(source, outname, settings, options=None):
img = _read_image(source)
original_format = img.format

if settings['copy_exif_data'] and settings['autorotate_images']:
logger.warning(
"The 'autorotate_images' and 'copy_exif_data' settings "
"are not compatible because Sigal can't save the "
"modified Orientation tag."
)

# Preserve EXIF data
if settings['copy_exif_data'] and _has_exif_tags(img):
copy_exif = settings['copy_exif_data'] and _has_exif_tags(img)
if copy_exif:
if options is not None:
options = deepcopy(options)
else:
options = {}
options['exif'] = img.info['exif']

if Exif is not None:
options['exif'] = img.getexif()
else:
options['exif'] = img.info['exif']

# Rotate the img, and catch IOError when PIL fails to read EXIF
if settings['autorotate_images']:
try:
img = Transpose().process(img)
except (OSError, IndexError):
pass
else:
if copy_exif and 'exif' in options:
# reset the orientation flag
options['exif'][0x0112] = 1

# Resize the image
if settings['img_processor']:
Expand Down
8 changes: 4 additions & 4 deletions sigal/plugins/watermark.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ def watermark(im, mark, position, opacity=1):
def add_watermark(img, settings=None):
logger = logging.getLogger(__name__)
logger.debug('Adding watermark to %r', img)
mark = Image.open(settings['watermark'])
position = settings.get('watermark_position', 'scale')
opacity = settings.get("watermark_opacity", 1)
return watermark(img, mark, position, opacity)
with Image.open(settings['watermark']) as mark:
position = settings.get('watermark_position', 'scale')
opacity = settings.get("watermark_opacity", 1)
return watermark(img, mark, position, opacity)


def register(settings):
Expand Down
Binary file added tests/sample/pictures/exifTest/lamas.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/test_extended_caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_save_cache(settings, tmpdir):

assert cache["exifTest/21.jpg"] == gal.albums["exifTest"].medias[0].exif
assert cache["exifTest/22.jpg"] == gal.albums["exifTest"].medias[1].exif
assert cache["exifTest/noexif.png"] == gal.albums["exifTest"].medias[2].exif
assert cache["exifTest/noexif.png"] is None


def test_restore_cache(settings, tmpdir):
Expand Down
54 changes: 29 additions & 25 deletions tests/test_image.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
from unittest.mock import patch

import PIL
import pytest
from packaging.version import Version
from PIL import Image as PILImage

from sigal import init_logging
Expand Down Expand Up @@ -41,8 +43,8 @@ def test_process_image(tmpdir):
image = Image(TEST_IMAGE, '.', settings)
status = process_image(image)
assert status == Status.SUCCESS
im = PILImage.open(os.path.join(str(tmpdir), TEST_IMAGE))
assert im.size == settings['img_size']
with PILImage.open(os.path.join(str(tmpdir), TEST_IMAGE)) as im:
assert im.size == settings['img_size']


def test_generate_image(tmpdir):
Expand All @@ -55,8 +57,8 @@ def test_generate_image(tmpdir):
)
options = None if i == 0 else {'quality': 85}
generate_image(SRCFILE, dstfile, settings, options=options)
im = PILImage.open(dstfile)
assert im.size == size
with PILImage.open(dstfile) as im:
assert im.size == size


def test_generate_image_imgformat(tmpdir):
Expand All @@ -72,8 +74,8 @@ def test_generate_image_imgformat(tmpdir):
)
options = {'quality': 85}
generate_image(SRCFILE, dstfile, settings, options=options)
im = PILImage.open(dstfile)
assert im.format == outfmt
with PILImage.open(dstfile) as im:
assert im.format == outfmt


def test_resize_image_portrait(tmpdir):
Expand All @@ -88,22 +90,21 @@ def test_resize_image_portrait(tmpdir):
portrait_dst = str(tmpdir.join(portrait_image))

generate_image(portrait_src, portrait_dst, settings)
im = PILImage.open(portrait_dst)

# In the default mode, PILKit resizes in a way to never make an image
# smaller than either of the lengths, the other is scaled accordingly.
# Hence we test that the shorter side has the smallest length.
assert im.size[0] == 200

landscape_image = 'KeckObservatory20071020.jpg'
landscape_src = os.path.join(
CURRENT_DIR, 'sample', 'pictures', 'dir2', landscape_image
)
landscape_dst = str(tmpdir.join(landscape_image))
with PILImage.open(portrait_dst) as im:
# In the default mode, PILKit resizes in a way to never make an image
# smaller than either of the lengths, the other is scaled accordingly.
# Hence we test that the shorter side has the smallest length.
assert im.size[0] == 200

landscape_image = 'KeckObservatory20071020.jpg'
landscape_src = os.path.join(
CURRENT_DIR, 'sample', 'pictures', 'dir2', landscape_image
)
landscape_dst = str(tmpdir.join(landscape_image))

generate_image(landscape_src, landscape_dst, settings)
im = PILImage.open(landscape_dst)
assert im.size[1] == 200
generate_image(landscape_src, landscape_dst, settings)
with PILImage.open(landscape_dst) as im:
assert im.size[1] == 200


@pytest.mark.parametrize(
Expand Down Expand Up @@ -157,13 +158,13 @@ def test_generate_thumbnail(tmpdir, image, path, wide_size, high_size):
dstfile = str(tmpdir.join(image))
for size in [(200, 150), (150, 200)]:
generate_thumbnail(path, dstfile, size)
im = PILImage.open(dstfile)
assert im.size == size
with PILImage.open(dstfile) as im:
assert im.size == size

for size, thumb_size in [((200, 150), wide_size), ((150, 200), high_size)]:
generate_thumbnail(path, dstfile, size, fit=False)
im = PILImage.open(dstfile)
assert im.size == thumb_size
with PILImage.open(dstfile) as im:
assert im.size == thumb_size


def test_get_exif_tags():
Expand Down Expand Up @@ -281,6 +282,9 @@ def test_exif_copy(tmpdir):
assert not simple


@pytest.mark.skipif(
Version(PIL.__version__) < Version('8.0'), reason='fails with Pillow < 8.0'
)
def test_exif_gps(tmpdir):
"""Test reading out correct geo tags"""

Expand Down
10 changes: 5 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
[tox]
envlist = py{37,38,39}-pillow{71,-latest},pypy3,check
envlist = py{37,38,39}-pillow{72,80,-latest},pypy3,check
skip_missing_interpreters = true
isolated_build = true

[gh-actions]
python =
3.7: py37-pillow71
3.8: py38-pillow-latest, check
3.7: py37-pillow72
3.8: py38-pillow80, check
3.9: py39-pillow-latest
pypy3: pypy3

[testenv]
deps =
pillow70: Pillow==7.0.0
pillow71: Pillow==7.1.0
pillow80: Pillow==8.0.1
pillow72: Pillow==7.2.0
extras =
all
tests
Expand Down