<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Vinay Jain</title>
    <description>The latest articles on Forem by Vinay Jain (@vinayjn).</description>
    <link>https://forem.com/vinayjn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F15644%2Fee1aadf6-826c-4f97-963e-adb51b63894b.jpg</url>
      <title>Forem: Vinay Jain</title>
      <link>https://forem.com/vinayjn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vinayjn"/>
    <language>en</language>
    <item>
      <title>How Haptik generates images on the fly with Python - Part 2</title>
      <dc:creator>Vinay Jain</dc:creator>
      <pubDate>Sun, 08 Apr 2018 17:24:18 +0000</pubDate>
      <link>https://forem.com/vinayjn/how-haptik-generates-images-on-the-fly-with-python---part-2-2fo9</link>
      <guid>https://forem.com/vinayjn/how-haptik-generates-images-on-the-fly-with-python---part-2-2fo9</guid>
      <description>&lt;p&gt;In the first post of this series, I introduced you to the basics of text drawing in Python by adding a greeting text on an image.&lt;br&gt;
I also highlighted examples of how I further extended this functionality to create some complex images at work. If you haven’t already read the &lt;a href="https://dev.to/vinayjn/how-haptik-generates-images-on-the-fly-with-python--1pnl"&gt;first part&lt;/a&gt; of this series (How Haptik generates images on the fly with Python), I recommend you take a glance at it first, to get a better understanding of this post.&lt;/p&gt;

&lt;p&gt;For now, we know how to draw text, change the font, and position the text on the image. In this post, we’ll discover how to draw multi-line text and also discuss the challenges of doing so.&lt;/p&gt;
&lt;h2&gt;
  
  
  Multiline Text
&lt;/h2&gt;

&lt;p&gt;Often, while generating images, we come across situations where the text doesn’t fit in a single line. Python Pillow is not helpful here as it doesn’t automatically draw &amp;amp; push the text to a new line. In order to do this manually, we need to calculate the width and height of the text. &lt;/p&gt;

&lt;p&gt;With the text-width, we determine when we need to move to the next line and with the text-height, we can figure how much space should be left in between the two lines:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgwm1pjbds9gmu6bvv7vp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fgwm1pjbds9gmu6bvv7vp.png" alt="Multiline Text with Pillow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea is to split the long sentences into multiple shorter sentences and draw each of these, one by one at the correct positions thereby making it look like a multi-line text. To split a longer line, we‘ll use a Pillow function to calculate the size of the text passed to it as one of the parameters.&lt;/p&gt;
&lt;h3&gt;
  
  
  Calculate text width
&lt;/h3&gt;

&lt;p&gt;For convenience, I’ve created a method &lt;code&gt;text_wrap()&lt;/code&gt; to explain the line-split logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ImageFont&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ImageDraw&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;text_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="c1"&gt;# If the width of the text is smaller than image width
&lt;/span&gt;    &lt;span class="c1"&gt;# we don't need to split it, just add it to the lines array
&lt;/span&gt;    &lt;span class="c1"&gt;# and return
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# split the line by spaces to get words
&lt;/span&gt;        &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="c1"&gt;# append every word to a line while its width is shorter than image width
&lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;         
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;max_width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                
                &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="c1"&gt;# when the line gets longer than the max width do not append the word, 
&lt;/span&gt;            &lt;span class="c1"&gt;# add the line to the lines array
&lt;/span&gt;            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;draw_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;    
    &lt;span class="c1"&gt;# open the background file
&lt;/span&gt;    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;background.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# size() returns a tuple of (width, height) 
&lt;/span&gt;    &lt;span class="n"&gt;image_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; 

    &lt;span class="c1"&gt;# create the ImageFont instance
&lt;/span&gt;    &lt;span class="n"&gt;font_file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fonts/Avenir-Medium.ttf&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ImageFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;truetype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font_file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# get shorter lines
&lt;/span&gt;    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;text_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="c1"&gt;# ['This could be a single line text ', 'but its too long to fit in one. ']
&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;__main__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;draw_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This could be a single line text but its too long to fit in one.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function expects three parameters – the &lt;strong&gt;text&lt;/strong&gt; to draw, an &lt;a href="http://pillow.readthedocs.io/en/3.1.x/reference/ImageFont.html#PIL.ImageFont.truetype" rel="noopener noreferrer"&gt;ImageFont&lt;/a&gt; class instance and the &lt;strong&gt;width&lt;/strong&gt; of the background image on which the text is to be drawn.&lt;/p&gt;

&lt;p&gt;The logic is pretty straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check, if the sentence can fit in one line then just return it without splitting, else:&lt;/li&gt;
&lt;li&gt;Split the sentence using spaces to fetch the words in it&lt;/li&gt;
&lt;li&gt;Create shorter lines by appending words while the width is smaller than the image width&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we run this script it returns an array containing 2 shorter lines which fit within the width of the background image.&lt;/p&gt;

&lt;p&gt;To draw these lines on the image we have to calculate the correct vertical position of each line. &lt;/p&gt;

&lt;h3&gt;
  
  
  Calculate Text Height
&lt;/h3&gt;

&lt;p&gt;Whenever we write text, there is an equal amount of space between two lines. For example in this post, the lines have the constant spaces between them. While building this library I faced an issue of varying spaces with most of the input text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This could be a single line text but it can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t fit in one line.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;text_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Output
# 62
# 51
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finding correct height for characters like &lt;strong&gt;g, j, p, q, y&lt;/strong&gt; which are drawn below the &lt;strong&gt;Baseline&lt;/strong&gt; and &lt;strong&gt;b, d, f, h, k, l&lt;/strong&gt;  which are drawn above the &lt;strong&gt;Median&lt;/strong&gt; is a little tedious due to varying heights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmnxe4qlkig4fom2m76mq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmnxe4qlkig4fom2m76mq.png" alt="Typography"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The best way to get the correct height of the text is to simply calculate the total height of &lt;strong&gt;“hg”.&lt;/strong&gt; This trick works because &lt;strong&gt;h&lt;/strong&gt; and &lt;strong&gt;g&lt;/strong&gt; cover the height range of all the English characters. &lt;/p&gt;

&lt;p&gt;For languages other than English, you might have to use different characters in place of &lt;strong&gt;h&lt;/strong&gt; &amp;amp; &lt;strong&gt;g&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This could be a single line text but it can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t fit in one line.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;text_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;line_height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;line_height&lt;/span&gt;

&lt;span class="c1"&gt;# Output
# 62
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we have our wrapped/short lines and also the text height we can draw these on the image. We can do this by keeping a reference to the vertical position of the previously drawn line and then adding to it the line height to calculate the vertical position of the new line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This could be a single line text but its too long to fit in one.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;text_wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;image_size&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;line_height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hg&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# draw the line on the image
&lt;/span&gt;    &lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# update the y position so that we can use it for next line
&lt;/span&gt;    &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;line_height&lt;/span&gt;
&lt;span class="c1"&gt;# save the image
&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;word2.png&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will output an image like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8w788euxu3pcw8arsfxq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8w788euxu3pcw8arsfxq.png" alt="Multiline Output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The text in the latter images looks much better and readable. At &lt;a href="https://haptik.ai" rel="noopener noreferrer"&gt;Haptik&lt;/a&gt;, we believe in experimentation and finding out the best possible way to solve problems. The above is one such example. In my next Blog post, I will be writing about how to center &lt;strong&gt;align text horizontally and vertically&lt;/strong&gt; in an image using Python.&lt;/p&gt;

&lt;p&gt;Think I did a good job? Let me know in the comments below.&lt;/p&gt;

&lt;p&gt;This post was originally written on the &lt;a href="https://haptik.ai/tech/putting-text-on-images-using-python%e2%80%8a-%e2%80%8apart2/" rel="noopener noreferrer"&gt;Haptik Tech Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>showdev</category>
      <category>webdev</category>
      <category>automation</category>
    </item>
    <item>
      <title>How Haptik generates images on the fly with Python</title>
      <dc:creator>Vinay Jain</dc:creator>
      <pubDate>Mon, 12 Mar 2018 11:26:18 +0000</pubDate>
      <link>https://forem.com/vinayjn/how-haptik-generates-images-on-the-fly-with-python--1pnl</link>
      <guid>https://forem.com/vinayjn/how-haptik-generates-images-on-the-fly-with-python--1pnl</guid>
      <description>&lt;p&gt;&lt;strong&gt;Computer graphics&lt;/strong&gt; teaches us how a pixel on a screen can be manipulated to draw beautiful shapes, artistic typography, eye-catching illustrations, &lt;strong&gt;‘make-me-look-good’&lt;/strong&gt; photo-filters and a lot more. Hardware manufacturers, researchers, software developers work together to build great products: smartphones, smartwatches, smart TVs, cameras all with the study of computer graphics.&lt;/p&gt;

&lt;p&gt;Despite the fact that computer graphics has evolved so fast and the development of software like Adobe Photoshop, Adobe Illustrator, Sketch has made our lives easier to a great extent, we still cannot generate images &lt;strong&gt;on-the-fly&lt;/strong&gt; with them. In order to do that, we’ll need to reach a level where there is no drag and drop, no fancy select-all-make-bold keyboard shortcuts, no cropping and no copying-pasting.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And we cannot get there by time-travel, but surely with code!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Come along, open your favorite text editor, follow me and I’ll help you draw dynamic text data on images. I assume you have &lt;strong&gt;&lt;a href="https://www.python.org/downloads/"&gt;Python&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://pypi.python.org/pypi/pip"&gt;pip&lt;/a&gt;&lt;/strong&gt; installed on your computer, but if not, follow the steps in the links to set up the development environment. After you’ve done setting up, from the shell, execute the below command to install &lt;strong&gt;Pillow&lt;/strong&gt; (more details &lt;a href="https://pypi.python.org/pypi/Pillow"&gt;here&lt;/a&gt;) and its dependencies.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pillow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you have installed all the dependencies, let's move forward and write some code. Pillow is an extensive library, but for our purpose, we’ll be using the following classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://pillow.readthedocs.io/en/3.1.x/reference/Image.html"&gt;Image&lt;/a&gt;: to create an image object for our greeting background&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://pillow.readthedocs.io/en/3.1.x/reference/ImageDraw.html"&gt;ImageDraw&lt;/a&gt;: creates a drawing context&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://pillow.readthedocs.io/en/3.1.x/reference/ImageFont.html#PIL.ImageFont.truetype"&gt;ImageFont&lt;/a&gt;: font of the text we will be drawing on the greeting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s take the following background image and initialize it with the following code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S7-3QoJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/background.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S7-3QoJs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/background.png" alt="Background"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# import required classes
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ImageDraw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ImageFont&lt;/span&gt;

&lt;span class="c1"&gt;# create an Image object with the input image
&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'background.png'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# initialize the drawing context with
# the image object as background
&lt;/span&gt;
&lt;span class="n"&gt;draw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ImageDraw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Draw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For creating ImageFont objects we also need font(ttf, otf) files, you can use any font of your choice, here I’ll be using the &lt;a href="https://fonts.google.com/specimen/Roboto"&gt;Roboto&lt;/a&gt; font which can be downloaded from the &lt;a href="https://github.com/google/fonts/tree/master/apache/roboto"&gt;Google Fonts GitHub&lt;/a&gt; repo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# create font object with the font file and specify
# desired size
&lt;/span&gt;
&lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ImageFont&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;truetype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Roboto-Bold.ttf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# starting position of the message
&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Happy Birthday!"&lt;/span&gt;
&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'rgb(0, 0, 0)'&lt;/span&gt; &lt;span class="c1"&gt;# black color
&lt;/span&gt;
&lt;span class="c1"&gt;# draw the message on the background
&lt;/span&gt;
&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'Vinay'&lt;/span&gt;
&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'rgb(255, 255, 255)'&lt;/span&gt; &lt;span class="c1"&gt;# white color
&lt;/span&gt;&lt;span class="n"&gt;draw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# save the edited image
&lt;/span&gt;
&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'greeting_card.png'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is what you get after executing the above code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--97FK3h0t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/greeting_card.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--97FK3h0t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i0.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/greeting_card.png" alt="Greeting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With some fonts, you might have to pass an optional parameter &lt;strong&gt;encoding&lt;/strong&gt; which tells the ImageFont module which encoding to use while opening the font file.&lt;/p&gt;

&lt;p&gt;Computer graphics have an inverted coordinate system, the &lt;strong&gt;origin(0, 0)&lt;/strong&gt; that lies at the top-left corner of the image. x here represents the distance of the text box from the left &lt;strong&gt;(x=0)&lt;/strong&gt; and &lt;em&gt;y&lt;/em&gt; represents the distance from the top &lt;strong&gt;(y=0)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While you save the image, you can pass optional parameters like &lt;strong&gt;optimize&lt;/strong&gt; and &lt;strong&gt;quality&lt;/strong&gt; to control the size of the output image.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image.save('optimized.png', optimize=True, quality=20)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This generates an output image &lt;strong&gt;optimized.png&lt;/strong&gt; with reduced quality but smaller size.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where Are We Using Pillow With Python?
&lt;/h3&gt;

&lt;p&gt;While at work, I recently developed a feature which demanded the creation of a leaderboard image &lt;strong&gt;on-the-fly&lt;/strong&gt;, with user-specific quiz score data. And just with a few lines of code, I was able to create an image like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L7LjTT9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/image_1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L7LjTT9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i2.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/image_1.png" alt="Haptik Weekly Quiz Leaderboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila! It looked great and we decided to use the idea of creating images on-the-go, for other use-cases as well. We currently use Pillow to generate images for Jokes, Motivational Quotes, Horoscopes, Word of the Day etc. in real time, and with data from different API responses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Th_m1xu1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i1.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/image_2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Th_m1xu1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i1.wp.com/haptik.ai/tech/wp-content/uploads/2018/01/image_2.png" alt="Haptik Motivational Quote &amp;amp; Word of the Day"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code we used in this post is not sufficient to draw text boxes as shown in the images above. I’ll be writing &lt;strong&gt;another post&lt;/strong&gt; which will focus on text alignment, splitting long text into multiple lines, controlling space between two lines and more.&lt;/p&gt;

&lt;p&gt;I recently read a &lt;a href="https://dev.to/ben/how-devto-dynamically-generates-social-images--2c2n"&gt;post&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/ben"&gt;@ben&lt;/a&gt;
 in which he wrote about how &lt;a href="https://dev.to"&gt;Dev.to&lt;/a&gt; generates social images dynamically. I realized I had done something similar at work so why not share with the community.&lt;/p&gt;

&lt;p&gt;Please do give me your feedback if any in the comments section below.&lt;/p&gt;

&lt;p&gt;This post was originally written for &lt;a href="https://haptik.ai/tech/putting-text-on-image-using-python/"&gt;Haptik Tech Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
