diff --git a/AUTHORS b/AUTHORS index 493aef0..c8364b9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ alphabetical order): - Christophe-Marie Duquesne - @datro - David Siroky +- Edwin Steele - François D. (@franek) - Giel van Schijndel - Jamie Starke diff --git a/sigal/gallery.py b/sigal/gallery.py index 3bd13ec..51c2ee8 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -5,6 +5,7 @@ # Copyright (c) 2014 - Jonas Kaufmann # Copyright (c) 2015 - François D. # Copyright (c) 2017 - Mate Lakat +# Copyright (c) 2018 - Edwin Steele # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -42,7 +43,8 @@ from os.path import isfile, join, splitext from . import image, video, signals from .compat import PY2, UnicodeMixin, strxfrm, url_quote, text_type, pickle -from .image import process_image, get_exif_tags, get_exif_data, get_size +from .image import (process_image, get_exif_tags, get_exif_data, get_size, + get_iptc_data) from .settings import get_thumb from .utils import (Devnull, copy, check_or_create_dir, url_from_path, read_markdown, cached_property, is_valid_html5_video, @@ -165,6 +167,23 @@ class Image(Media): return (get_exif_tags(self.raw_exif, datetime_format=datetime_format) if self.raw_exif and self.ext in ('.jpg', '.jpeg') else None) + def _get_metadata(self): + super(Image, self)._get_metadata() + # If a title or description hasn't been obtained by other means, look + # for the information in IPTC fields as catalogued in: + # https://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata + if not self.title or not self.description: + iptc_data = get_iptc_data(self.src_path) + if iptc_data and not self.title: + # 2:05 is the IPTC title property + if (2, 5) in iptc_data: + self.title = iptc_data[(2, 5)].decode('utf-8') + + if iptc_data and not self.description: + # 2:120 is the IPTC description property + if (2, 120) in iptc_data: + self.description = iptc_data[(2, 120)].decode('utf-8') + @cached_property def raw_exif(self): try: diff --git a/sigal/image.py b/sigal/image.py index 864f3be..597894e 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -2,6 +2,7 @@ # Copyright (c) 2009-2016 - Simon Conseil # Copyright (c) 2015 - François D. +# Copyright (c) 2018 - Edwin Steele # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -43,6 +44,7 @@ from datetime import datetime from PIL.ExifTags import TAGS, GPSTAGS from PIL import Image as PILImage from PIL import ImageOps +from PIL import IptcImagePlugin from pilkit.processors import Transpose from pilkit.utils import save_image @@ -235,6 +237,13 @@ def get_exif_data(filename): return data +def get_iptc_data(filename): + """Return a dict with the raw IPTC data.""" + + img = _read_image(filename) + return IptcImagePlugin.getiptcinfo(img) + + def dms_to_degrees(v): """Convert degree/minute/second to decimal degrees.""" diff --git a/tests/sample/pictures/iptcTest/1.jpg b/tests/sample/pictures/iptcTest/1.jpg new file mode 100644 index 0000000..6184e7b Binary files /dev/null and b/tests/sample/pictures/iptcTest/1.jpg differ diff --git a/tests/sample/pictures/iptcTest/2.jpg b/tests/sample/pictures/iptcTest/2.jpg new file mode 100644 index 0000000..6184e7b Binary files /dev/null and b/tests/sample/pictures/iptcTest/2.jpg differ diff --git a/tests/sample/pictures/iptcTest/2.md b/tests/sample/pictures/iptcTest/2.md new file mode 100644 index 0000000..417e534 --- /dev/null +++ b/tests/sample/pictures/iptcTest/2.md @@ -0,0 +1,3 @@ +Title: Markdown title beats iptc + +Markdown description beats iptc diff --git a/tests/sample/pictures/iptcTest/index.md b/tests/sample/pictures/iptcTest/index.md new file mode 100644 index 0000000..e063aba --- /dev/null +++ b/tests/sample/pictures/iptcTest/index.md @@ -0,0 +1,3 @@ +Title: iptc test gallery + +## This is used by test_image.py and test_gallery.py to validate iptc handling diff --git a/tests/test_gallery.py b/tests/test_gallery.py index 67019b1..22bbe19 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -111,6 +111,22 @@ def test_media_orig(settings, tmpdir): assert m.big == '21.jpg' +def test_media_iptc_override(settings): + img_with_md = Image('2.jpg', 'iptcTest', settings) + assert img_with_md.title == "Markdown title beats iptc" + # Markdown parsing adds formatting. Let's just focus on content + assert "Markdown description beats iptc" in img_with_md.description + img_no_md = Image('1.jpg', 'iptcTest', settings) + assert img_no_md.title == 'Haemostratulus clouds over Canberra - ' + \ + '2005-12-28 at 03-25-07' + assert img_no_md.description == \ + '"Haemo" because they look like haemoglobin ' + \ + 'cells and "stratulus" because I can\'t work out whether ' + \ + 'they\'re Stratus or Cumulus clouds.\nWe\'re driving down ' + \ + 'the main drag in Canberra so it\'s Parliament House that ' + \ + 'you can see at the end of the road.' + + def test_image(settings, tmpdir): settings['destination'] = str(tmpdir) settings['datetime_format'] = '%d/%m/%Y' diff --git a/tests/test_image.py b/tests/test_image.py index 0f65911..7fea0b6 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -7,7 +7,7 @@ from PIL import Image from sigal import init_logging from sigal.image import (generate_image, generate_thumbnail, get_exif_tags, - get_exif_data, get_size, process_image) + get_exif_data, get_size, process_image, get_iptc_data) from sigal.settings import create_settings, Status CURRENT_DIR = os.path.dirname(__file__) @@ -162,6 +162,27 @@ def test_get_exif_tags(): assert 'gps' not in simple +def test_get_iptc_data(): + test_image = '1.jpg' + src_file = os.path.join(CURRENT_DIR, 'sample', 'pictures', 'iptcTest', + test_image) + data = get_iptc_data(src_file) + # Title + assert data[(2, 5)] == b'Haemostratulus clouds over Canberra - ' + \ + b'2005-12-28 at 03-25-07' + # Description + assert data[(2, 120)] == b'"Haemo" because they look like haemoglobin ' + \ + b'cells and "stratulus" because I can\'t work out whether ' + \ + b'they\'re Stratus or Cumulus clouds.\nWe\'re driving down ' + \ + b'the main drag in Canberra so it\'s Parliament House that ' + \ + b'you can see at the end of the road.' + # This file has no IPTC data + test_image = '21.jpg' + src_file = os.path.join(CURRENT_DIR, 'sample', 'pictures', 'exifTest', + test_image) + assert get_iptc_data(src_file) is None + + def test_iso_speed_ratings(): data = {'ISOSpeedRatings': ()} simple = get_exif_tags(data)