Skip to content

Commit

Permalink
Príprava generovaných obrázkov pre fb
Browse files Browse the repository at this point in the history
  • Loading branch information
mireq committed Nov 16, 2019
1 parent e3dc6b1 commit f4136f3
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 0 deletions.
2 changes: 2 additions & 0 deletions image_renderer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
default_app_config = 'image_renderer.apps.AppConfig'
7 changes: 7 additions & 0 deletions image_renderer/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig as BaseAppConfig


class AppConfig(BaseAppConfig):
name = 'image_renderer'
verbose_name = "Image renderer"
11 changes: 11 additions & 0 deletions image_renderer/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from django.urls import path

from . import views


app_name = 'image_renderer'

urlpatterns = [
path('<slug:image_type>/<int:content_type>/<path:object_id>.png', views.RenderImageView.as_view(), name='render'),
]
138 changes: 138 additions & 0 deletions image_renderer/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*-
import os

from PIL import Image, ImageFont
from django.contrib.contenttypes.models import ContentType
from django.http.response import HttpResponseNotFound, HttpResponse
from django.shortcuts import get_object_or_404
from django.views.generic import View
from pil_text_block import Block


STATIC_DIR = os.path.join(os.path.dirname(__file__), '..', 'static')


def get_fallback_response():
if get_fallback_response.image_cache is None:
with open(os.path.join(STATIC_DIR, 'images', 'share_text_fallback.png'), 'rb') as fallback_fp:
get_fallback_response.image_cache = fallback_fp.read()
return HttpResponse(get_fallback_response.image_cache, content_type='image/png')
get_fallback_response.image_cache = None


class BaseRenderer(object):
_backgrund_image = None

def __init__(self, image_type, content_type, content_object):
self.image_type = image_type
self.content_type = content_type
self.content_object = content_object

def get_background_image(self):
if self._backgrund_image is None:
path = os.path.join(STATIC_DIR, 'images', 'share_text_background.png')
self._backgrund_image = Image.open(path)
return self._backgrund_image.convert('RGBA')

def render_layout(self, bg, layout):
return self.render_node(bg, (0, 0) + bg.size, layout)

def render_node(self, bg, window, node):
node = node.copy()
node_type = node.pop('type')
return getattr(self, f'render_{node_type}')(bg, window, **node)

def render_row(self, bg, window, children=None):
children = [] if children is None else children
for child in children:
child_window = self.render_node(bg, window, child)
window = (window[0], window[1] + child_window[3], window[2], window[3] - child_window[3])
return window

def render_text(self, bg, window, text, width=None, height=None):
width = window[2] if width is None else width
height = window[3] if height is None else height
font = ImageFont.truetype(os.path.join(STATIC_DIR, 'fonts', 'OpenSans', 'OpenSans-ExtraBold.ttf'), size=75)
block = Block(text, (width, height), color='#ffffff', font=font, ellipsis='…')
result = block.render()
bg.alpha_composite(result.image, dest=(window[0], window[1]))
window = (window[0], window[1], result.size[0], result.size[1])
return window

def render_canvas(self, bg, window, width=None, height=None): # pylint: disable=unused-argument
width = window[2] if width is None else width
height = window[3] if height is None else height
window = (window[0], window[1], width, height)
return window

def render(self):
raise NotImplementedError()


class TextRenderer(BaseRenderer):
def __init__(self, image_type, content_type, content_object, content_field, title_field):
self.content = getattr(content_object, content_field)
self.title = getattr(content_object, title_field)
super().__init__(image_type, content_type, content_object)

def render(self):
bg = self.get_background_image()
layout = {
'type': 'row',
'children': [
{
'type': 'canvas',
'height': 20,
},
{
'type': 'text',
'height': 103,
'text': self.title,
},
{
'type': 'canvas',
'height': 20,
},
{
'type': 'text',
'text': 'World'
},
{
'type': 'canvas',
'height': 20,
},
]
}
self.render_layout(bg, layout)
response = HttpResponse(content_type='image/png')
bg.convert('RGB').save(response, format='PNG')
return response


RENDERERS = {
('opengraph', 'news', 'news'): (TextRenderer, {'title_field': 'title', 'content_field': 'short_text'}),
}


class RendererFactory():
@staticmethod
def get_renderer(image_type, content_type, content_object):
try:
renderer_class, kwargs = RENDERERS[(image_type, content_type.app_label, content_type.model)]
except KeyError:
return None
return renderer_class(image_type, content_type, content_object, **kwargs)


class RenderImageView(View):
def get(self, request, image_type, content_type, object_id):
content_type = get_object_or_404(ContentType, pk=content_type)
try:
content_object = get_object_or_404(content_type.model_class(), pk=object_id)
except ValueError:
return HttpResponseNotFound()
renderer = RendererFactory.get_renderer(image_type, content_type, content_object)
if renderer is None:
return get_fallback_response()
else:
return renderer.render()
Binary file added static/fonts/OpenSans/OpenSans-ExtraBold.ttf
Binary file not shown.
Binary file added static/fonts/OpenSans/OpenSans-Regular.ttf
Binary file not shown.
Binary file added static/images/share_text_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions static/images/share_text_background.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added static/images/share_text_fallback.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions web/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
url(r'^api/editor/', include('rich_editor.urls')),
url(r'^hijack/', include('hijack.urls')),
url(r'^django-email-log/', include('django_email_log.urls')),
url(r'^image/', include('image_renderer.urls')),
url(r'^favicon.ico$', RedirectView.as_view(url=staticfiles_storage.url('images/favicon/favicon.ico'), permanent=True)),
]

Expand Down

0 comments on commit f4136f3

Please sign in to comment.