From d626679c23dbf9d48edec4730b84c144e8ae6401 Mon Sep 17 00:00:00 2001 From: Alexander Schier Date: Sat, 29 Mar 2014 00:47:17 +0100 Subject: [PATCH 1/4] added support for multiline text drawing --- PIL/ImageDraw.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 18f9c0c00..11fc52ec7 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -257,6 +257,9 @@ class ImageDraw(object): # Draw text. def text(self, xy, text, fill=None, font=None, anchor=None): + if "\n" in text: + return self.multiline_text(xy, text, fill, font, anchor) + ink, fill = self._getink(fill) if font is None: font = self.getfont() @@ -273,10 +276,49 @@ class ImageDraw(object): mask = font.getmask(text) self.draw.draw_bitmap(xy, mask, ink) + def multiline_text(self, xy, text, fill=None, font=None, anchor=None, + spacing=0, align="left"): + widths, heights = [], [] + max_width = 0 + lines = text.split("\n") + for line in lines: + line_width, line_height = self.textsize(line, font) + widths.append(line_width) + max_width = max(max_width, line_width) + heights.append(line_height) + left, top = xy + for idx, line in enumerate(lines): + if align == "left": + pass # left = x + elif align == "center": + left += (max_width - widths[idx]) / 2.0 + elif align == "right": + left += (max_width - widths[idx]) + else: + assert False, 'align must be either "left", "center" or "right"' + self.text((left, top), + line, fill, font, anchor) + top += heights[idx] + spacing + left = xy[0] + + def multiline_textsize(self, text, font=None, spacing=0): + max_width = 0 + height = 0 + lines = text.split("\n") + for line in lines: + line_width, line_height = self.textsize(line, font) + height += line_height + spacing + max_width = max(max_width, line_width) + return max_width, height + + ## # Get the size of a given string, in pixels. def textsize(self, text, font=None): + if "\n" in text: + return multiline_textsize(text, font) + if font is None: font = self.getfont() return font.getsize(text) From 77169b2fdba9546c9f6f3842a48c7ba2ee370b16 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 13:21:39 +1000 Subject: [PATCH 2/4] Moved multiline split character to common functions --- PIL/ImageDraw.py | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/PIL/ImageDraw.py b/PIL/ImageDraw.py index 11fc52ec7..2afe93714 100644 --- a/PIL/ImageDraw.py +++ b/PIL/ImageDraw.py @@ -256,8 +256,18 @@ class ImageDraw(object): ## # Draw text. + def _multiline_check(self, text): + split_character = "\n" if isinstance(text, type("")) else b"\n" + + return split_character in text + + def _multiline_split(self, text): + split_character = "\n" if isinstance(text, type("")) else b"\n" + + return text.split(split_character) + def text(self, xy, text, fill=None, font=None, anchor=None): - if "\n" in text: + if self._multiline_check(text): return self.multiline_text(xy, text, fill, font, anchor) ink, fill = self._getink(fill) @@ -277,10 +287,10 @@ class ImageDraw(object): self.draw.draw_bitmap(xy, mask, ink) def multiline_text(self, xy, text, fill=None, font=None, anchor=None, - spacing=0, align="left"): + spacing=0, align="left"): widths, heights = [], [] max_width = 0 - lines = text.split("\n") + lines = self._multiline_split(text) for line in lines: line_width, line_height = self.textsize(line, font) widths.append(line_width) @@ -289,40 +299,38 @@ class ImageDraw(object): left, top = xy for idx, line in enumerate(lines): if align == "left": - pass # left = x + pass # left = x elif align == "center": left += (max_width - widths[idx]) / 2.0 elif align == "right": left += (max_width - widths[idx]) else: - assert False, 'align must be either "left", "center" or "right"' - self.text((left, top), - line, fill, font, anchor) + assert False, 'align must be "left", "center" or "right"' + self.text((left, top), line, fill, font, anchor) top += heights[idx] + spacing left = xy[0] - def multiline_textsize(self, text, font=None, spacing=0): - max_width = 0 - height = 0 - lines = text.split("\n") - for line in lines: - line_width, line_height = self.textsize(line, font) - height += line_height + spacing - max_width = max(max_width, line_width) - return max_width, height - - ## # Get the size of a given string, in pixels. def textsize(self, text, font=None): - if "\n" in text: - return multiline_textsize(text, font) + if self._multiline_check(text): + return self.multiline_textsize(text, font) if font is None: font = self.getfont() return font.getsize(text) + def multiline_textsize(self, text, font=None, spacing=0): + max_width = 0 + height = 0 + lines = self._multiline_split(text) + for line in lines: + line_width, line_height = self.textsize(line, font) + height += line_height + spacing + max_width = max(max_width, line_width) + return max_width, height + ## # A simple 2D drawing interface for PIL images. From 9546fac7eca6562039d8b5ad5eafa9eeeefa219e Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 13:22:04 +1000 Subject: [PATCH 3/4] Added multiline documentation --- docs/reference/ImageDraw.rst | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/reference/ImageDraw.rst b/docs/reference/ImageDraw.rst index e6d5c36ee..e030147e9 100644 --- a/docs/reference/ImageDraw.rst +++ b/docs/reference/ImageDraw.rst @@ -232,17 +232,39 @@ Methods Draws the string at the given position. :param xy: Top left corner of the text. - :param text: Text to be drawn. + :param text: Text to be drawn. If it contains any newline characters, + the text is passed on to mulitiline_text() :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. :param fill: Color to use for the text. +.. py:method:: PIL.ImageDraw.Draw.multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left") + + Draws the string at the given position. + + :param xy: Top left corner of the text. + :param text: Text to be drawn. If it contains any newline characters, + the text is split and passed on to mulitiline_text() + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: The number of pixels between lines. + :param align: "left", "center" or "right". + .. py:method:: PIL.ImageDraw.Draw.textsize(text, font=None) Return the size of the given string, in pixels. - :param text: Text to be measured. + :param text: Text to be measured. If it contains any newline characters, + the text is passed on to mulitiline_textsize() :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. +.. py:method:: PIL.ImageDraw.Draw.multiline_textsize(text, font=None, spacing=0) + + Return the size of the given string, in pixels. + + :param text: Text to be measured. If it contains any newline characters, + the text is split and passed on to mulitiline_textsize() + :param font: An :py:class:`~PIL.ImageFont.ImageFont` instance. + :param spacing: The number of pixels between lines. + Legacy API ---------- From b7335ec9d9396bcf9c14331769ec1a614b1717ff Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Thu, 18 Jun 2015 17:51:33 +1000 Subject: [PATCH 4/4] Added multiline tests --- Tests/images/multiline_text_center.png | Bin 0 -> 2845 bytes Tests/images/multiline_text_right.png | Bin 0 -> 2846 bytes Tests/images/multiline_text_spacing.png | Bin 0 -> 2844 bytes Tests/test_imagefont.py | 69 +++++++++++++++++++++++- 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Tests/images/multiline_text_center.png create mode 100644 Tests/images/multiline_text_right.png create mode 100644 Tests/images/multiline_text_spacing.png diff --git a/Tests/images/multiline_text_center.png b/Tests/images/multiline_text_center.png new file mode 100644 index 0000000000000000000000000000000000000000..f44d0783a0944986efdc62d4f0bcc7530b74ab5b GIT binary patch literal 2845 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 OU$EoXD23%}hqtl13=Tu}sJ^Cc_Y74%tdX zcFx$B5VA9}XNk;UlJD)D@1Jvh*LnW9ujjh&=Xsy|`rY^M_ukPr%?t&>C&3^PNDyhH zX9)sv!GOIR-yy)3xl$=05O@};r~O-C`szfmk=2wm??!j`1^am`%e%%ADVl%eS|o_Y z#u^k^2TeC{B^bH&5nAIUFI>1Fm$cMLxoDiLte_CX z@ia8#g}UKz=jG+2ZAHbzwmEEX9V5UQY?{2!7tuu1cv$e8LU<$k(g`Y+8n8Mldkj3n zGc@&Jsy4`wuMz9*V@P}+6BF~}$B&mU&s@A%QdwDnM#&|$ zyG2Jw3knK?Bf>b|k=?t$Bl*^%M9xw=tME-tO*}k2pBXzlJ9P6*0)gN+re|PKN7(LY zo~<8P%Fqnj+}y0Zr>v|TclsLBQ&Wi3-qTZ@pMR9Z7*}(7v(p&PJC1ol+!*%R!2p<; zqEPR=tnS>Yb76o3eEVM^y(eqFu~-=K2pwlv*V#|`R=x>Tgv9R8{h+iobf|1YVAg&# zb9wM}RaMo{Iq9`Qqn3t-Czkmrb91LEzuY?|>nkh%MarE0WHOnC2p(nb718NfXJ=^!+H^}rRfOyl$i}yB-D1?*<@}}~24*~yP;pZp zi4jxmp=k_{k3Um^R?;#uq;%R^-udlDW z``jYFY#{#T`Q*+yQ&UreoIdoW%U7;+&iRp4w%*+xP*70V<8V@?A?&pY#RMZhXmD`w z(1OpC(2luI%A?)sRvRslXlJ2qnTLnRI9>V)KfSc{(zRZtmoiln@Tfv}Bl` zm*sWg~x~H&aDJO;(nXPXKrT;QFDux*Jnwkb^Wa8Sp3{=V8U` zW3lI;GEk^?PTz}e4B1%h=+Oi+d2nj#U~hZn^XJ=>Y~g1q8uT-d0t1Oe;xREX`D5TT zK|ZLGl9KmS-9&w8eSEw*3WW*?P~#Lf%gEO)j{wAas%kN8km^4r<{6U-SN?* zT_c)^4ymVJ!Xm_2Qlw@UPi(jmgm0 z*Ei3&?&}Nzry~;5|3x)K4DUb-|qLnwtDjO-;>ljH9EY_>|js zLhS?Z??}s>LPHsdL~IKHjtXgMX(>}HG$1PjKs_od3O`)D&0+}&2`%6MAbQU1wnOu~ zP#7SdH~5y8mtDq0%^Jidq$*VR_77jz1w$GFbpUhq*KX$q81gSvZaA==avXz_(z<^@ zGZDcqKX+#1}Ym7FN-*xmP1-p%yhK1p)1w^c|bKBt{-E#9!jQBmpC(uP!k36{QUg# zHX*6~)95HQC1w6=#xA=ronKKwVRLIMJS+@oCMJ_ePnUlp9ayPCOrq)SwwtXT`$wN(G;=@Bj*4EZ=hsv906JWPXZ)Dy4 zI$3)P+vJRs%W{AN{URW6>ck13EL~2%F1+-bxGCYN`{*$ZJwLy<#<^fY!E#%k^)Xo= zRj!yk%wEXX;buv%SH!~RNMS{?F^T-%YobZ!bW8a;coo1+VB{ zMrXjcM3BCfin~^gj9cxaElhi&thgct?iho-+I8DZ=btzKj6HP)7%>W)op0w%!RsI| z&*K9=3p?@K)`#rBt&Fa`aR$BCgXlMr%T`Jw3Ry)up#5Rg?`J#S$z%AkZk`Yk+1Q*j zQL{-WQC69XTrqDkk^39u=YwNo8bK^i61fc9+X7_msb=rA&1B*s>L^LSr`>OG{iO2) zd6l}TOhj#YxheuN>2mc>1*wGldD`t zth2kd0EqMcx7k)e5Zq5~sPaeWe1abTe46xEojP2ifOF+`_Yzin04!brSd=w~>=?Ig zvf0BP)ca=tTF=e(T!xS>P$gVslxPne^-DB&f zDOOikxBEyV9#5;iL`r6V@qtlg0b`tIv*^b_;Q4l%Soh ztqWbLDiaeE;o;$SMFG7TnmSn@KPdhSf&Sn9B(&iGbjiUYwk-+n`74anH`6QDL5KYp DTe*8% literal 0 HcmV?d00001 diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 88858c717..8414584f9 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -10,6 +10,8 @@ import copy FONT_PATH = "Tests/fonts/FreeMono.ttf" FONT_SIZE = 20 +TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward" + try: from PIL import ImageFont @@ -95,7 +97,7 @@ try: txt = "Hello World!" ttf = ImageFont.truetype(font, FONT_SIZE) ttf.getsize(txt) - + img = Image.new("RGB", (256, 64), "white") d = ImageDraw.Draw(img) d.text((10, 10), txt, font=ttf, fill='black') @@ -134,7 +136,7 @@ try: draw = ImageDraw.Draw(im) ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) line_spacing = draw.textsize('A', font=ttf)[1] + 4 - lines = ['hey you', 'you are awesome', 'this looks awkward'] + lines = TEST_TEXT.split("\n") y = 0 for line in lines: draw.text((0, y), line, font=ttf) @@ -148,6 +150,69 @@ try: # at epsilon = ~38. self.assert_image_similar(im, target_img, .5) + def test_render_multiline_text(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Test that text() correctly connects to multiline_text() + # and that align defaults to left + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.text((0, 0), TEST_TEXT, font=ttf) + + target = 'Tests/images/multiline_text.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + + # Test align center and right + for align, ext in {"center": "_center", + "right": "_right"}.items(): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) + + target = 'Tests/images/multiline_text'+ext+'.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + + def test_unknown_align(self): + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + # Act/Assert + self.assertRaises(AssertionError, lambda: draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align="unknown")) + + def test_multiline_size(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + + # Test that textsize() correctly connects to multiline_textsize() + self.assertEqual(draw.textsize(TEST_TEXT, font=ttf), + draw.multiline_textsize(TEST_TEXT, font=ttf)) + + def test_multiline_width(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + + self.assertEqual(draw.textsize("longest line", font=ttf)[0], + draw.multiline_textsize("longest line\nline", font=ttf)[0]) + + def test_multiline_spacing(self): + ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) + + im = Image.new(mode='RGB', size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) + + target = 'Tests/images/multiline_text_spacing.png' + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, .5) + def test_rotated_transposed_font(self): img_grey = Image.new("L", (100, 100)) draw = ImageDraw.Draw(img_grey)