Skip to content

Commit

Permalink
Merge pull request #787 from Sefaria/social-images
Browse files Browse the repository at this point in the history
Social images
  • Loading branch information
EliezerIsrael authored Mar 18, 2022
2 parents 6ec5e2c + 4918810 commit ace29ef
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 2 deletions.
36 changes: 36 additions & 0 deletions reader/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
from sefaria.helper.topic import get_topic, get_all_topics, get_topics_for_ref, get_topics_for_book
from sefaria.helper.community_page import get_community_page_items
from sefaria.helper.file import get_resized_file
from sefaria.image_generator import make_img_http_response
import sefaria.tracker as tracker

if USE_VARNISH:
Expand Down Expand Up @@ -1493,6 +1494,41 @@ def protected_post(request):

return jsonResponse({"error": "Unsupported HTTP method."}, callback=request.GET.get("callback", None))

@catch_error_as_json
@csrf_exempt
def social_image_api(request, tref):
lang = request.GET.get("lang", "en")
if lang == "bi":
lang = "en"
version = request.GET.get("ven", None) if lang == "en" else request.GET.get("vhe", None)
platform = request.GET.get("platform", "twitter")

try:
ref = Ref(tref)
ref_str = ref.normal() if lang == "en" else ref.he_normal()

if version:
version = version.replace("_", " ")

tf = TextFamily(ref, stripItags=True, lang=lang, version=version, context=0, commentary=False).contents()

he = tf["he"] if type(tf["he"]) is list else [tf["he"]]
en = tf["text"] if type(tf["text"]) is list else [tf["text"]]

text = en if lang == "en" else he
text = ' '.join(text)
cat = tf["primary_category"]

except:
text = None
cat = None
ref_str = None


res = make_img_http_response(text, cat, ref_str, lang, platform)

return res


@catch_error_as_json
@csrf_exempt
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,4 @@ unicodecsv==0.14.1
unidecode==1.1.1
user-agents==2.2.0
babel
python-bidi
158 changes: 158 additions & 0 deletions sefaria/image_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
from PIL import Image, ImageDraw, ImageFont
import textwrap
from bidi.algorithm import get_display
import re
from django.http import HttpResponse
import io

palette = { # [(bg), (font)]
"Commentary": [(75, 113, 183), (255, 255, 255)],
"Tanakh": [(0, 78, 95), (255, 255, 255)],
"Midrash": [(93, 149, 111), (255, 255, 255)],
"Mishnah": [(90, 153, 183), (0, 0, 0)],
"Talmud": [(204, 180, 121), (0, 0, 0)],
"Halakhah": [(128, 47, 62), (255, 255, 255)],
"Kabbalah": [(89, 65, 118), (255, 255, 255)],
"Jewish Thought": [(127, 133, 169), (0, 0, 0)],
"Liturgy": [(171, 78, 102), (255, 255, 255)],
"Tosefta": [(0, 130, 127), (255, 255, 255)],
"Chasidut": [(151, 179, 134), (0, 0, 0)],
"Musar": [(124, 65, 111), (255, 255, 255)],
"Responsa": [(203, 97, 88), (255, 255, 255)],
"Quoting Commentary": [(203, 97, 88), (255, 255, 255)],
"Sheets": [(24, 52, 93), (255, 255, 255)],
"Sheet": [(24, 52, 93), (255, 255, 255)],
"Targum": [(59, 88, 73), (255, 255, 255)],
"Modern Commentary": [(184, 212, 211), (255, 255, 255)],
"Reference": [(212, 137, 108), (255, 255, 255)],
"System": [(24, 52, 93), (255, 255, 255)]
}

platforms = {
"facebook": {
"width": 1200,
"height": 630,
"padding": 260,
"font_size": 60,
"ref_font_size": 24,
"he_spacing": 5,
},
"twitter": {
"width": 1200,
"height": 600,
"padding": 260,
"font_size": 60,
"ref_font_size": 24,
"he_spacing": 5,
}

}

def smart_truncate(content, length=180, suffix='...'):
if len(content) <= length:
return content
else:
return ' '.join(content[:length+1].split(' ')[0:-1]) + suffix

def calc_letters_per_line(text, font, img_width):
avg_char_width = sum(font.getsize(char)[0] for char in text) / len(text)
max_char_count = int(img_width / avg_char_width )
return max_char_count

def cleanup_and_format_text(text, language):
#removes html tags, nikkudot and taamim.
text = text.replace('<br>', ' ')
cleanr = re.compile('<.*?>')
text = re.sub(cleanr, '', text)
text = text.replace("—", "-")
text = text.replace(u"\u05BE", " ") #replace hebrew dash with ascii

strip_cantillation_vowel_regex = re.compile("[^\u05d0-\u05f4\s^\x00-\x7F\x80-\xFF\u0100-\u017F\u0180-\u024F\u1E00-\u1EFF\u2000-\u206f]")
text = strip_cantillation_vowel_regex.sub('', text)
text = smart_truncate(text)
return text


def generate_image(text="", category="System", ref_str="", lang="he", platform="twitter"):
text_color = palette[category][1]
bg_color = palette[category][0]

font = ImageFont.truetype(font='static/fonts/Amiri-Taamey-Frank-merged.ttf', size=platforms[platform]["font_size"])
width = platforms[platform]["width"]
height = platforms[platform]["height"]
padding_x = platforms[platform]["padding"]
padding_y = padding_x/2
img = Image.new('RGBA', (width, height), color=bg_color)


if lang == "en":
align = "left"
logo_url = "static/img/logo.png"
spacing = 0
ref_font = ImageFont.truetype(font='static/fonts/Roboto-Regular.ttf', size=platforms[platform]["ref_font_size"])
cat_border_pos = (0, 0, 0, img.size[1])

else:
align = "right"
logo_url = "static/img/logo-hebrew.png"
spacing = platforms[platform]["he_spacing"]
ref_font = ImageFont.truetype(font='static/fonts/Heebo-Regular.ttf', size=platforms[platform]["ref_font_size"])
cat_border_pos = (img.size[0], 0, img.size[0], img.size[1])

text = cleanup_and_format_text(text, lang)
text = textwrap.fill(text=text, width= calc_letters_per_line(text, font, int(img.size[0]-padding_x)))
text = get_display(text) # Applies BIDI algorithm to text so that letters aren't reversed in PIL.

draw = ImageDraw.Draw(im=img)
draw.text(xy=(img.size[0] / 2, img.size[1] / 2), text=text, font=font, spacing=spacing, align=align,
fill=text_color, anchor='mm')


#category line
draw.line(cat_border_pos, fill=palette[category][0], width=int(width*.02))

#header white
draw.line((0, int(height*.05), img.size[0], int(height*.05)), fill=(255, 255, 255), width=int(height*.1))
draw.line((0, int(height*.1), img.size[0], int(height*.1)), fill="#CCCCCC", width=int(height*.0025))

#write ref
draw.text(xy=(img.size[0] / 2, img.size[1]-padding_y/2), text=get_display(ref_str.upper()), font=ref_font, spacing=spacing, align=align, fill=text_color, anchor='mm')


#border
draw.line((0, 0, width, 0), fill="#666666", width=1)
draw.line((0, 0, 0, height), fill="#666666", width=1)
draw.line((width-1, 0, width-1, height), fill="#666666", width=1)
draw.line((0, height-1, width, height-1), fill="#666666", width=1)


#add sefaria logo
logo = Image.open(logo_url)
logo.thumbnail((width, int(height*.06)))
logo_padded = Image.new('RGBA', (width, height))
logo_padded.paste(logo, (int(width/2-logo.size[0]/2), int(height*.05-logo.size[1]/2)))

img = Image.alpha_composite(img, logo_padded)


return(img)

def make_img_http_response(text, category, ref_str, lang, platform):
try:
img = generate_image(text, category, ref_str, lang, platform)
except Exception as e:
print(e)
height = platforms[platform]["height"]
width = platforms[platform]["width"]
img = Image.new('RGBA', (width, height), color="#18345D")
logo = Image.open("static/img/logo-white.png")
logo.thumbnail((400, 400))
logo_padded = Image.new('RGBA', (width, height))
logo_padded.paste(logo, (int(width/2-logo.size[0]/2), int(height/2-logo.size[1]/2)))
img = Image.alpha_composite(img, logo_padded)

buf = io.BytesIO()
img.save(buf, format='png')

res = HttpResponse(buf.getvalue(), content_type="image/png")
return res
5 changes: 5 additions & 0 deletions sefaria/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,11 @@
url(r'^random/?$', reader_views.random_text_page),
]

# Preview Images
urlpatterns += [
url(r'^api/img-gen/(?P<tref>.+)$', reader_views.social_image_api),
]

# Chavruta URLs
urlpatterns += [
url(r'^beit-midrash/(?P<slug>[^.]+)$', reader_views.beit_midrash),
Expand Down
Binary file added static/fonts/Amiri-Taamey-Frank-merged.ttf
Binary file not shown.
Binary file added static/fonts/Heebo-Regular.ttf
Binary file not shown.
Binary file added static/fonts/Roboto-Regular.ttf
Binary file not shown.
Binary file added static/img/logo-hebrew-white.png
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/img/logo-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
{% endblock %}

{% block ogimage %}
<meta property="og:image" content="https://embed.sefaria.org{{ request.path }}?lang={{request.GET.lang}}&platform=facebook" />
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}/api/img-gen{{ request.path }}?lang={{request.GET.lang}}&platform=facebook&ven={{request.GET.ven}}&vhe={{request.GET.vhe}}" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
Expand All @@ -43,7 +43,7 @@
<meta name="twitter:site" content="@sefariaproject" />
<meta name="twitter:title" content="{{title|striptags}}" />
<meta name="twitter:description" content="{% block soc_description %}{{ desc|striptags }}{% endblock %}" />
<meta name="twitter:image" content="https://embed.sefaria.org{{ request.path }}?lang={{request.GET.lang}}&platform=twitter">
<meta name="twitter:image" content="{{ request.scheme }}://{{ request.get_host }}/api/img-gen{{ request.path }}?lang={{request.GET.lang}}&platform=twitter&ven={{request.GET.ven}}&vhe={{request.GET.vhe}}" />

<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-itunes-app" content="app-id=1163273965">
Expand Down

0 comments on commit ace29ef

Please sign in to comment.