diff --git a/reader/views.py b/reader/views.py
index 27cfd5ab5d..db410d7f72 100644
--- a/reader/views.py
+++ b/reader/views.py
@@ -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:
@@ -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
diff --git a/requirements.txt b/requirements.txt
index 416bb386b9..42cf1259be 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -59,3 +59,4 @@ unicodecsv==0.14.1
unidecode==1.1.1
user-agents==2.2.0
babel
+python-bidi
\ No newline at end of file
diff --git a/sefaria/image_generator.py b/sefaria/image_generator.py
new file mode 100644
index 0000000000..e7ec9f0fba
--- /dev/null
+++ b/sefaria/image_generator.py
@@ -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('
', ' ')
+ 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
diff --git a/sefaria/urls.py b/sefaria/urls.py
index a9415c3249..288679c029 100644
--- a/sefaria/urls.py
+++ b/sefaria/urls.py
@@ -305,6 +305,11 @@
url(r'^random/?$', reader_views.random_text_page),
]
+# Preview Images
+urlpatterns += [
+ url(r'^api/img-gen/(?P.+)$', reader_views.social_image_api),
+]
+
# Chavruta URLs
urlpatterns += [
url(r'^beit-midrash/(?P[^.]+)$', reader_views.beit_midrash),
diff --git a/static/fonts/Amiri-Taamey-Frank-merged.ttf b/static/fonts/Amiri-Taamey-Frank-merged.ttf
new file mode 100644
index 0000000000..a17548a505
Binary files /dev/null and b/static/fonts/Amiri-Taamey-Frank-merged.ttf differ
diff --git a/static/fonts/Heebo-Regular.ttf b/static/fonts/Heebo-Regular.ttf
new file mode 100644
index 0000000000..332f96efe0
Binary files /dev/null and b/static/fonts/Heebo-Regular.ttf differ
diff --git a/static/fonts/Roboto-Regular.ttf b/static/fonts/Roboto-Regular.ttf
new file mode 100644
index 0000000000..3d6861b423
Binary files /dev/null and b/static/fonts/Roboto-Regular.ttf differ
diff --git a/static/img/logo-hebrew-white.png b/static/img/logo-hebrew-white.png
new file mode 100644
index 0000000000..b5b3f6f49d
Binary files /dev/null and b/static/img/logo-hebrew-white.png differ
diff --git a/static/img/logo-white.png b/static/img/logo-white.png
new file mode 100644
index 0000000000..130d0e20d0
Binary files /dev/null and b/static/img/logo-white.png differ
diff --git a/templates/base.html b/templates/base.html
index 4fcfcb8f82..662143b285 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -30,7 +30,7 @@
{% endblock %}
{% block ogimage %}
-
+
@@ -43,7 +43,7 @@
-
+