Skip to content

Commit

Permalink
Add watermark support
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Jun 29, 2017
1 parent 0264a98 commit 34f30f0
Show file tree
Hide file tree
Showing 14 changed files with 78 additions and 15 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -10,6 +10,7 @@ cache:
env:
global:
- RANDOM_SEED=12345
- REGENERATE_IMAGES=true
- VALIDATE_LINKS=true

install:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,9 @@
# Revision History

## 5.1

- Added `?watermark=<str>` option to include name of supported clients.

## 5.0

- Removed `/api/magic` route.
Expand Down
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -107,6 +107,7 @@ doctor: ## Confirm system dependencies are available
echo "GOOGLE_ANALYTICS_TID=local" >> $@
echo "#REGENERATE_IMAGES=true" >> $@
echo "#VALIDATE_LINKS=true" >> $@
echo "WATERMARK_OPTIONS=localhost" >> $@

# PROJECT DEPENDENCIES #########################################################

Expand Down
3 changes: 3 additions & 0 deletions app.json
Expand Up @@ -22,6 +22,9 @@
},
"TWITTER_IMAGE_WIDTH": {
"required": true
},
"WATERMARK_OPTIONS": {
"required": true
}
},
"addons":[
Expand Down
2 changes: 1 addition & 1 deletion memegen/__init__.py
Expand Up @@ -3,7 +3,7 @@
import sys

__project__ = 'memegen.link'
__version__ = '5.0'
__version__ = '5.1'

VERSION = "{} v{}".format(__project__, __version__)

Expand Down
20 changes: 13 additions & 7 deletions memegen/domain/image.py
Expand Up @@ -12,22 +12,24 @@ class Image(object):
"""JPEG generated by applying text to a template."""

def __init__(self, template, text, root=None,
style=None, font=None, size=None):
style=None, font=None, size=None, watermark=""):
self.root = root
self.template = template
self.style = style
self.text = text
self.font = font
self.width = size.get('width') if size else None
self.height = size.get('height') if size else None
self.watermark = watermark

@property
def path(self):
if not self.root:
return None

base = os.path.join(self.root, self.template.key, self.text.path)
custom = [self.style, self.font, self.width, self.height]
custom = [self.style, self.font,
self.width, self.height, self.watermark]

if any(custom):
slug = self.hash(custom)
Expand All @@ -48,6 +50,7 @@ def save(self):
font=self.font.path,
background=self.template.get_path(self.style),
width=self.width, height=self.height,
watermark=self.watermark,
)

directory = os.path.dirname(self.path)
Expand All @@ -60,11 +63,7 @@ def save(self):
return path


# The following Pillow image functions are based on:
# https://github.com/danieldiekmeier/memegenerator


def _generate(top, bottom, font, background, width, height):
def _generate(top, bottom, font, background, width, height, watermark):
"""Add text to an image and save it."""
log.info("Loading background: %s", background)
background_image = ImageFile.open(background)
Expand Down Expand Up @@ -130,6 +129,13 @@ def _generate(top, bottom, font, background, width, height):
if width and height:
image = _add_blurred_background(image, background_image, width, height)

# Add watermark
if watermark:
draw = ImageDraw.Draw(image)
watermark_font = ImageFont.truetype(font, 15)
_draw_outlined_text(draw, (3, image.size[1] - 20),
watermark, watermark_font, 15)

return image


Expand Down
20 changes: 14 additions & 6 deletions memegen/routes/image.py
Expand Up @@ -21,6 +21,7 @@
'share': fields.Bool(missing=False),
'width': fields.Int(missing=None),
'height': fields.Int(missing=None),
'watermark': fields.Str(missing=None),
}


Expand Down Expand Up @@ -60,8 +61,9 @@ def get_without_text_jpeg(key):

@blueprint.route("/<key>/<path:path>.jpg", endpoint='get')
@flaskparser.use_kwargs(OPTIONS)
def get_with_text(key, path, alt, font, preview, share, **size):
options = dict(key=key, path=path, alt=alt, font=font, **size)
def get_with_text(key, path, alt, font, watermark, preview, share, **size):
options = dict(key=key, path=path,
alt=alt, font=font, watermark=watermark, **size)
if preview:
options['preview'] = True
if share:
Expand All @@ -79,16 +81,22 @@ def get_with_text(key, path, alt, font, preview, share, **size):
options.pop('alt')
return redirect(route('.get', **options))

if path != text.path:
options['path'] = text.path
return redirect(route('.get', **options))

if font and not fontfile:
options.pop('font')
return redirect(route('.get', **options))

if path != text.path:
options['path'] = text.path
if watermark and watermark not in app.config['WATERMARK_OPTIONS']:
options.pop('watermark')
return redirect(route('.get', **options))

image = app.image_service.create(template, text,
style=alt, font=fontfile, size=size)
image = app.image_service.create(
template, text,
style=alt, font=fontfile, size=size, watermark=watermark,
)

if not preview:
cache_filtered.add(key=key, path=path, alt=alt, font=font)
Expand Down
7 changes: 7 additions & 0 deletions memegen/settings.py
@@ -1,5 +1,7 @@
import os

from . import __project__


class Config:
"""Base configuration."""
Expand Down Expand Up @@ -28,6 +30,7 @@ class Config:
REMOTE_TRACKING_URL = os.getenv('REMOTE_TRACKING_URL')
TWITTER_IMAGE_HEIGHT = os.getenv('TWITTER_IMAGE_HEIGHT', 401)
TWITTER_IMAGE_WIDTH = os.getenv('TWITTER_IMAGE_WIDTH', 801)
WATERMARK_OPTIONS = os.getenv('WATERMARK_OPTIONS', "").split(',')


class ProdConfig(Config):
Expand All @@ -47,6 +50,8 @@ class TestConfig(Config):
DEBUG = True
TESTING = True

WATERMARK_OPTIONS = ['test']


class DevConfig(Config):
"""Development configuration."""
Expand All @@ -55,6 +60,8 @@ class DevConfig(Config):

DEBUG = True

WATERMARK_OPTIONS = ['dev', 'localhost', '127.0.0.1']


def get_config(name):
assert name, "No configuration specified"
Expand Down
Binary file added tests/examples/watermark-pad-h.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/examples/watermark-pad-v.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/examples/watermark.jpg
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_api_root.py
Expand Up @@ -16,6 +16,6 @@ def it_returns_links_and_metadata(client):
'fonts': "http://localhost/api/fonts/",
'aliases': "http://localhost/api/aliases/",
'search': "http://localhost/api/search/",
'version': "5.0",
'version': "5.1",
'changes': "https://raw.githubusercontent.com/jacebrowning/memegen/master/CHANGELOG.md"
}
22 changes: 22 additions & 0 deletions tests/test_images.py
Expand Up @@ -166,6 +166,28 @@ def it_keeps_size_after_redirect(client):
expect(load(response, as_json=False)).contains(
'height=89')

def describe_watermark():

def it_accept_supported_watermark(client):
response = client.get("/iw/test.jpg?watermark=test")

expect(response.status_code) == 200
expect(response.mimetype) == 'image/jpeg'

def it_redirects_with_unsupported_watermark(client):
response = client.get("/iw/test.jpg?watermark=unsupported")

expect(response.status_code) == 302
expect(load(response, as_json=False)).contains(
'<a href="/iw/test.jpg"')

def it_keeps_watermark_after_redirect(client):
response = client.get("/iw/test 2.jpg?watermark=test")

expect(response.status_code) == 302
expect(load(response, as_json=False)).contains(
'<a href="/iw/test_2.jpg?watermark=test"')

def describe_preview():

def it_keeps_flag_after_redirect(client):
Expand Down
11 changes: 11 additions & 0 deletions tests/test_images_examples.py
Expand Up @@ -68,3 +68,14 @@ def test_custom_sizes(client):
("size-both.jpg", "/fry/hello/world.jpg?width=200&height=300"),
]:
save_image(client, url, name)


@pytest.mark.skipif(**unset('REGENERATE_IMAGES'))
def test_forced_watermark(client):
"""Create meme with a watermark."""
for name, partial in [
("watermark.jpg", "/fry/hello/world.jpg?"),
("watermark-pad-h.jpg", "/fry/hello/world.jpg?width=300&height=200&"),
("watermark-pad-v.jpg", "/fry/hello/world.jpg?width=200&height=300&"),
]:
save_image(client, partial + "watermark=test", name)

0 comments on commit 34f30f0

Please sign in to comment.