add watermark app, templatetag, templates

remotes/origin/mitri4
Dmitriy Shesterkin 9 years ago
parent e169d609da
commit 1be1ffdf24
  1. 2
      batiskaf/jinja2.py
  2. 1
      batiskaf/jinja2_ext/currency.py
  3. 6
      batiskaf/jinja2_ext/watermarks.py
  4. 1
      batiskaf/settings.py
  5. 2
      batiskaf/templates/jinja2/includes/category_category_thumb.jinja
  6. 2
      batiskaf/templates/jinja2/includes/category_product_thumb.jinja
  7. 2
      batiskaf/templates/jinja2/index.jinja
  8. 6
      batiskaf/templates/jinja2/product.jinja
  9. 2
      batiskaf/templates/jinja2/promo/list.jinja
  10. BIN
      static/watermarks/logo-all-small.png
  11. BIN
      static/watermarks/logo-small-45.png
  12. 1
      watermarker/__init__.py
  13. 15
      watermarker/admin.py
  14. 168
      watermarker/core.py
  15. 34
      watermarker/migrations/0001_initial.py
  16. 0
      watermarker/migrations/__init__.py
  17. 52
      watermarker/models.py

@ -4,6 +4,7 @@ from django.core.urlresolvers import reverse
from batiskaf.jinja2_ext.thumbnails import thumbnail
from batiskaf.jinja2_ext.cart import cart
from batiskaf.jinja2_ext.currency import currency
from batiskaf.jinja2_ext.watermarks import watermark
from batiskaf.jinja2_ext.html_filters import *
from jinja2 import Environment
from store.models import Category, Brand, Currency
@ -22,6 +23,7 @@ def environment(**options):
env.filters['thumbnail'] = thumbnail
env.filters['cart'] = cart
env.filters['currency'] = currency
env.filters['watermark'] = watermark
env.filters['bootstrap'] = bootstrap
env.filters['bootstrap_inline'] = bootstrap_inline
env.filters['bootstrap_horizontal'] = bootstrap_horizontal

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from store.currency import Currency

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from watermarker import core
def watermark(url, wm):
return core.watermark(url, wm)

@ -62,6 +62,7 @@ INSTALLED_APPS = (
'django.contrib.sitemaps',
'django.contrib.sites',
#'django_extensions',
'watermarker',
'rest_framework',
'easy_thumbnails',
'bootstrapform_jinja',

@ -2,7 +2,7 @@
<div class="thumbnail">
{% set im = child.image|thumbnail("420x420") %}
<a href="{{ child.get_absolute_url() }}">
<img src="/static/{{ im.url }}"
<img src="/static/{{ im.url|watermark('medium-photo') }}"
class="img-responsive" alt="Купить {{ child.title }}"
title="Купить {{ child.title }}" width="210" height="210"></a>
<div class="caption"><br>

@ -7,7 +7,7 @@
{% endif %}
{% set im = product.main_image()|thumbnail("420x420") %}
<a href="{{ product.get_absolute_url() }}">
<img src="/static/{{ im.url }}"
<img src="/static/{{ im.url|watermark('medium-photo') }}"
class="img-responsive" alt="Купить {{ product.title }}"
title="Купить {{ product.title }}" width="210" height="210"></a>
<div class="caption">

@ -36,7 +36,7 @@
<div class="col-md-3 col-xs-3 col-sm-3 col-lg-3">
<div class="thumbnail">
{% set im = product.main_image()|thumbnail("420x420") %}
<a href="{{ product.get_absolute_url() }}"><img src="/static/{{ im.url }}"
<a href="{{ product.get_absolute_url() }}"><img src="/static/{{ im.url|watermark('medium-photo') }}"
class="img-responsive"
alt="Купить {{ product.title }}"
title="Купить {{ product.title }}"></a>

@ -36,9 +36,9 @@
<div class="product-big-thumbnail-container">
{% set im = product.main_image()|thumbnail("398x398") %}
<!-- <a href="/static/{{ product.main_image().url }}" id="product-big-image-url"> -->
<img src="/static/{{ im.url }}" alt="{{ product.title }}" title="{{ product.title }}" width="398"
<img src="/static/{{ im.url|watermark('medium-photo')}}" alt="{{ product.title }}" title="{{ product.title }}" width="398"
height="392" id='product-big-img' class=""
data-zoom-image='/static/{{ product.main_image().url }}'/>
data-zoom-image='/static/{{ product.main_image().url|watermark('big-photo')}}'/>
<!-- </a> -->
</div>
<br>
@ -149,7 +149,7 @@
<div class="col-md-3 col-xs-3 col-sm-3 col-lg-3">
<div class="thumbnail">
{% set im = product.main_image()|thumbnail("420x420") %}
<a href="{{ product.get_absolute_url() }}"><img src="/static/{{ im.url }}"
<a href="{{ product.get_absolute_url() }}"><img src="/static/{{ im.url|watermark('medium-photo') }}"
class="img-responsive"
alt="Купить {{ product.title }}"
title="Купить {{ product.title }}"></a>

@ -39,7 +39,7 @@
</p>
<p class="text-center">
{# {% set im = object.image|thumbnail("400x400") %}#}
<a href="{{ url('promo_detail', args=(object.slug,)) }}"><img src="/static/{{ object.image.url }}"
<a href="{{ url('promo_detail', args=(object.slug,)) }}"><img src="/static/{{ object.image.url|watermark('medium-photo') }}"
alt="{{ object.title }}"
title="{{ object.title }}"
style="max-width: 100%"/></a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

@ -0,0 +1 @@
__version__ = '1.3.3'

@ -0,0 +1,15 @@
from django.contrib import admin
from watermarker import models
class WatermarkAdmin(admin.ModelAdmin):
list_display = ('title', 'get_position', 'opacity', 'is_active')
search_fields = ('title',)
fieldsets = (
(None, {'fields': ('title', 'mark', 'opacity', 'position', ('x', 'y'), 'is_active', 'update_hard'),
'classes': ('wide',)}),
)
admin.site.register(models.Watermark, WatermarkAdmin)

@ -0,0 +1,168 @@
# coding: utf-8
import os
import urllib
from logging import error
from django.db.models.fields.files import ImageFieldFile, ImageField
from django.conf import settings
from PIL import Image, ImageEnhance
from watermarker import models
def reduce_opacity(im, opacity):
"""Returns an image with reduced opacity."""
assert 0 <= 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 make_watermark(im, mark, position, opacity=1, shift=(0, 0)):
"""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))
elif position == 'br':
x = max(im.size[0] - mark.size[0], 0) - shift[0]
y = max(im.size[1] - mark.size[1], 0) - shift[1]
layer.paste(mark, (x, y))
elif position == 'tr':
x = max(im.size[0] - mark.size[0], 0) - shift[0]
y = 0 + shift[1]
layer.paste(mark, (x, y))
elif position == 'bl':
x = 0 + shift[0]
y = max(im.size[1] - mark.size[1], 0) - shift[1]
layer.paste(mark, (x, y))
elif position == 'tl':
x = 0 + shift[0]
y = 0 + shift[1]
layer.paste(mark, (x, y))
else:
layer.paste(mark, (0, 0))
# composite the watermark with the layer
return Image.composite(layer, im, layer)
def get_path(url):
"""Makes a filesystem path from the specified URL"""
if url.startswith(settings.MEDIA_URL):
root = settings.MEDIA_ROOT
else:
root = settings.STATIC_ROOT
url = url.replace(settings.MEDIA_URL, '').replace(settings.STATIC_URL, '')
url = urllib.parse.unquote(url)
return os.path.normpath(os.path.join(root, url))
def make_iff_instance(path):
""" Returns ImageFieldFile for provided image path """
return ImageFieldFile(instance=None, field=ImageField(), name=path)
def watermark(url, wm):
# return usual url(string) or ImageFieldFile
result_as_iff = False
if isinstance(wm, models.Watermark):
watermark = wm
else:
try:
watermark = models.Watermark.objects.get(title=wm, is_active=True)
except (models.Watermark.DoesNotExist, models.Watermark.MultipleObjectsReturned):
return url
if not watermark.is_active:
return url
# to work not only with strings
if isinstance(url, ImageFieldFile):
result_as_iff = True
if hasattr(url, 'url'):
url = url.url
else:
return url
basedir = '%s/watermarked' % os.path.dirname(url)
base, ext = os.path.splitext(os.path.basename(url))
# watermarked url for template
wm_url = os.path.join(basedir, '%s%s' % (base, ext))
# path to save watermarked img
wm_path = get_path(wm_url)
# not to do more than we need
if os.path.exists(wm_path) and not watermark.update_hard:
if result_as_iff:
return make_iff_instance(wm_path)
return wm_url
# create a folder for watermarks if needed
wm_dir = os.path.dirname(wm_path)
if not os.path.exists(wm_dir):
os.makedirs(wm_dir)
img_path = get_path(url)
if not os.path.exists(img_path):
return url
try:
img = Image.open(img_path)
mark = Image.open(watermark.mark.path)
x = watermark.x or 0
y = watermark.y or 0
shift = (x, y)
position = watermark.position
opacity = watermark.opacity
marked_img = make_watermark(img, mark, position, opacity, shift)
marked_img.save(wm_path)
except Exception as e:
error("Cant create watermark for %s. Error type: %s. Msg: %s" % (img_path, type(e), e))
return url
if result_as_iff:
return make_iff_instance(wm_path)
return wm_url
def get_url_safe(path):
""" If we save wm with result_as_iff == True, we thumbnail returns us full path to picture (not valid url)
may be there is prettier way to manage this
"""
if not path.startswith(settings.MEDIA_URL):
return os.path.join(settings.MEDIA_URL, path.split(settings.MEDIA_URL)[-1])
return path

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.3 on 2017-05-05 15:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Watermark',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=32, unique=True, verbose_name='Заголовок')),
('mark', models.ImageField(upload_to='watermarks', verbose_name='Водный знак')),
('opacity', models.FloatField(default=1, help_text='Значение должно быть от 0 до 1', verbose_name='Непрозрачность')),
('position', models.CharField(choices=[('tile', 'плиточно'), ('scale', 'масштабировано'), ('br', 'нижний правый угол'), ('tr', 'верхний правый угол'), ('bl', 'нижний левый угол'), ('tl', 'верхний левый угол')], max_length=8, verbose_name='Расположение')),
('x', models.IntegerField(blank=True, default=0, null=True, verbose_name='Отступ по оси Х')),
('y', models.IntegerField(blank=True, default=0, null=True, verbose_name='Отступ по оси У')),
('is_active', models.BooleanField(default=True, verbose_name='Активна')),
('update_hard', models.BooleanField(default=False, help_text='Используйте, если хотите обновить уже созданные ватермарки. Бьет по производительности', verbose_name='Агрессивно обновлять ватермарки')),
],
options={
'verbose_name': 'Водный знак',
'verbose_name_plural': 'Водные знаки',
},
),
]

@ -0,0 +1,52 @@
from django.db import models
from django.core.exceptions import ValidationError
class Watermark(models.Model):
OPACITY_ERROR_TEXT = 'Непрозрачность должна быть должна быть между 0 и 1. Это правило!'
TILE = 'tile'
SCALE = 'scale'
BR = 'br'
TR = 'tr'
BL = 'bl'
TL = 'tl'
POSITIONS = (
(TILE, 'плиточно'),
(SCALE, 'масштабировано'),
(BR, 'нижний правый угол'),
(TR, 'верхний правый угол'),
(BL, 'нижний левый угол'),
(TL, 'верхний левый угол'),
)
title = models.CharField(max_length=32, verbose_name='Заголовок', unique=True)
mark = models.ImageField(upload_to='watermarks', verbose_name='Водный знак')
opacity = models.FloatField(default=1, verbose_name='Непрозрачность', help_text='Значение должно быть от 0 до 1')
position = models.CharField(max_length=8, verbose_name='Расположение', choices=POSITIONS)
x = models.IntegerField(blank=True, null=True, verbose_name='Отступ по оси Х', default=0)
y = models.IntegerField(blank=True, null=True, verbose_name='Отступ по оси У', default=0)
is_active = models.BooleanField(default=True, verbose_name='Активна')
update_hard = models.BooleanField(default=False, verbose_name='Агрессивно обновлять ватермарки',
help_text='Используйте, если хотите обновить уже созданные ватермарки. '
'Бьет по производительности')
def get_position(self):
return self.get_position_display()
def clean(self):
if not 0 <= self.opacity <= 1:
raise ValidationError(self.OPACITY_ERROR_TEXT)
def __unicode__(self):
return self.title
def __str__(self):
return self.title
get_position.short_description = 'Расположение'
class Meta:
verbose_name = 'Водный знак'
verbose_name_plural = 'Водные знаки'
Loading…
Cancel
Save