diff --git a/AUTHORS b/AUTHORS index b18a156..894bb7d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,4 @@ alphabetical order): - Matthias Vogelgesang - Vikram Shirgur - Yuce Tekol +- Abdul Qabiz diff --git a/docs/plugins.rst b/docs/plugins.rst index d191aac..558ae12 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -103,3 +103,8 @@ Upload to S3 plugin =================== .. automodule:: sigal.plugins.upload_s3 + +Watermark plugin +=================== + +.. automodule:: sigal.plugins.watermark diff --git a/sigal/plugins/watermark.py b/sigal/plugins/watermark.py new file mode 100644 index 0000000..0478859 --- /dev/null +++ b/sigal/plugins/watermark.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2005 - Shane Hathaway (http://code.activestate.com/recipes/362879-watermark-with-pil/) +# Copyright (c) 2015 - Abdul Qabiz + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +"""Plugin which adds a watermark to the image. + +Settings: + +- ``watermark``: path to the watermark image. +- ``watermark_position``: the watermark position either 'scale' or 'tile' or + a 2-tuple giving the upper left corner, or + a 4-tuple defining the left, upper, right, and lower pixel coordinate, or + `None` (same as (0, 0)). + If a 4-tuple is given, the size of the pasted image must match the size of the region. +- ``watermark_opacity``: the watermark opacity (0.0 to 1.0). + +""" + + +import logging +from PIL import ImageDraw, Image, ImageEnhance +from sigal import signals + +logger = logging.getLogger(__name__) + + +def reduce_opacity(im, opacity): + """Returns an image with reduced opacity.""" + assert opacity >= 0 and opacity <= 1 + if im.mode != 'RGBA': + im = im.convert('RGBA') + else: + im = im.copy() + alpha = im.split()[3] + alpha = ImageEnhance.Brightness(alpha).enhance(opacity) + im.putalpha(alpha) + return im + + +def watermark(im, mark, position, opacity=1): + """Adds a watermark to an image.""" + if opacity < 1: + mark = reduce_opacity(mark, opacity) + if im.mode != 'RGBA': + im = im.convert('RGBA') + # create a transparent layer the size of the image and draw the + # watermark in that layer. + layer = Image.new('RGBA', im.size, (0, 0, 0, 0)) + if position == 'tile': + for y in range(0, im.size[1], mark.size[1]): + for x in range(0, im.size[0], mark.size[0]): + layer.paste(mark, (x, y)) + elif position == 'scale': + # scale, but preserve the aspect ratio + ratio = min( + float(im.size[0]) / mark.size[0], float(im.size[1]) / mark.size[1]) + w = int(mark.size[0] * ratio) + h = int(mark.size[1] * ratio) + mark = mark.resize((w, h)) + layer.paste(mark, ((im.size[0] - w) / 2, (im.size[1] - h) / 2)) + else: + layer.paste(mark, position) + # composite the watermark with the layer + return Image.composite(layer, im, layer) + + +def add_watermark(img, settings=None): + 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) + + +def register(settings): + if settings.get('watermark'): + signals.img_resized.connect(add_watermark) + else: + logger.warning('Watermark image is not set') diff --git a/sigal/settings.py b/sigal/settings.py index f47cf8b..e6518cc 100644 --- a/sigal/settings.py +++ b/sigal/settings.py @@ -66,6 +66,7 @@ _DEFAULT_CONFIG = { 'title': '', 'use_orig': False, 'video_size': (480, 360), + 'watermark': '', 'webm_options': ['-crf', '10', '-b:v', '1.6M', '-qmin', '4', '-qmax', '63'], 'write_html': True, @@ -124,7 +125,7 @@ def read_settings(filename=None): if k not in ['__builtins__']) # Make the paths relative to the settings file - paths = ['source', 'destination'] + paths = ['source', 'destination', 'watermark'] if os.path.isdir(join(settings_path, settings['theme'])): paths.append('theme') diff --git a/tests/sample/sigal.conf.py b/tests/sample/sigal.conf.py index 34bb333..a8fd029 100644 --- a/tests/sample/sigal.conf.py +++ b/tests/sample/sigal.conf.py @@ -10,10 +10,12 @@ keep_orig = True links = [('Example link', 'http://example.org'), ('Another link', 'http://example.org')] -plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright'] +plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright', 'sigal.plugins.watermark'] copyright = u"© An example copyright message" adjust_options = {'color': 0.0, 'brightness': 1.0, 'contrast': 1.0, 'sharpness': 0.0} - +watermark = "watermark.png" +watermark_position = "tile" +watermark_opacity = 0.3 # theme = 'galleria' # thumb_size = (280, 210) diff --git a/tests/sample/watermark.png b/tests/sample/watermark.png new file mode 100644 index 0000000..083dfa1 Binary files /dev/null and b/tests/sample/watermark.png differ